Introduce asynchronous RestconfServer.readData()
[netconf.git] / restconf / restconf-nb / src / test / java / org / opendaylight / restconf / nb / rfc8040 / rests / services / impl / RestconfDataServiceImplTest.java
index 7764d17280938e365368771fe9b3aa300f7e71f9..29cd849a9e4c63a1a01f654b06e67a4e30cb59da 100644 (file)
@@ -7,12 +7,12 @@
  */
 package org.opendaylight.restconf.nb.rfc8040.rests.services.impl;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertThrows;
-import static org.junit.Assert.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
@@ -21,8 +21,10 @@ import static org.opendaylight.yangtools.util.concurrent.FluentFutures.immediate
 import static org.opendaylight.yangtools.util.concurrent.FluentFutures.immediateFluentFuture;
 import static org.opendaylight.yangtools.util.concurrent.FluentFutures.immediateTrueFluentFuture;
 
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
 import java.net.URI;
-import java.util.Collection;
+import java.nio.charset.StandardCharsets;
 import java.util.List;
 import java.util.Optional;
 import java.util.Set;
@@ -36,6 +38,7 @@ import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
 import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnitRunner;
 import org.opendaylight.mdsal.common.api.CommitInfo;
@@ -44,26 +47,20 @@ import org.opendaylight.mdsal.dom.api.DOMActionService;
 import org.opendaylight.mdsal.dom.api.DOMDataBroker;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadTransaction;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadWriteTransaction;
-import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteTransaction;
 import org.opendaylight.mdsal.dom.api.DOMMountPoint;
 import org.opendaylight.mdsal.dom.api.DOMMountPointService;
+import org.opendaylight.mdsal.dom.api.DOMRpcService;
 import org.opendaylight.mdsal.dom.api.DOMSchemaService;
-import org.opendaylight.mdsal.dom.api.DOMTransactionChain;
 import org.opendaylight.mdsal.dom.spi.FixedDOMSchemaService;
 import org.opendaylight.netconf.dom.api.NetconfDataTreeService;
-import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
 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.nb.rfc8040.AbstractJukeboxTest;
 import org.opendaylight.restconf.nb.rfc8040.databind.DatabindContext;
+import org.opendaylight.restconf.nb.rfc8040.databind.DatabindProvider;
 import org.opendaylight.restconf.nb.rfc8040.legacy.NormalizedNodePayload;
-import org.opendaylight.restconf.nb.rfc8040.rests.services.api.RestconfStreamsSubscriptionService;
-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.restconf.nb.rfc8040.streams.StreamsConfiguration;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.patch.rev170222.yang.patch.yang.patch.Edit.Operation;
 import org.opendaylight.yangtools.yang.common.ErrorTag;
 import org.opendaylight.yangtools.yang.common.ErrorType;
@@ -71,23 +68,27 @@ 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.ContainerNode;
 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
-import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
-import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 
 @RunWith(MockitoJUnitRunner.StrictStubs.class)
 public class RestconfDataServiceImplTest extends AbstractJukeboxTest {
-    private ContainerNode buildBaseCont;
-    private ContainerNode buildBaseContConfig;
-    private ContainerNode buildBaseContOperational;
-    private RestconfDataServiceImpl dataService;
-    private ContainerNode buildPlayerCont;
-    private ContainerNode buildLibraryCont;
-    private MapNode buildPlaylistList;
+    private static final NodeIdentifier PLAYLIST_NID = new NodeIdentifier(PLAYLIST_QNAME);
+    private static final NodeIdentifier LIBRARY_NID = new NodeIdentifier(LIBRARY_QNAME);
+
+    // config contains one child the same as in operational and one additional
+    private static final ContainerNode CONFIG_JUKEBOX = Builders.containerBuilder()
+            .withNodeIdentifier(new NodeIdentifier(JUKEBOX_QNAME))
+            .withChild(CONT_PLAYER)
+            .withChild(Builders.containerBuilder().withNodeIdentifier(LIBRARY_NID).build())
+            .build();
+    // operational contains one child the same as in config and one additional
+    private static final ContainerNode OPER_JUKEBOX = Builders.containerBuilder()
+            .withNodeIdentifier(new NodeIdentifier(JUKEBOX_QNAME))
+            .withChild(CONT_PLAYER)
+            .withChild(Builders.mapBuilder().withNodeIdentifier(PLAYLIST_NID).build())
+            .build();
 
-    @Mock
-    private DOMTransactionChain domTransactionChain;
     @Mock
     private UriInfo uriInfo;
     @Mock
@@ -95,8 +96,6 @@ public class RestconfDataServiceImplTest extends AbstractJukeboxTest {
     @Mock
     private DOMDataTreeReadTransaction read;
     @Mock
-    private DOMDataTreeWriteTransaction write;
-    @Mock
     private DOMMountPointService mountPointService;
     @Mock
     private DOMMountPoint mountPoint;
@@ -107,62 +106,36 @@ public class RestconfDataServiceImplTest extends AbstractJukeboxTest {
     @Mock
     private DOMActionService actionService;
     @Mock
-    private RestconfStreamsSubscriptionService delegRestconfSubscrService;
+    private DOMRpcService rpcService;
     @Mock
     private MultivaluedMap<String, String> queryParamenters;
     @Mock
     private AsyncResponse asyncResponse;
+    @Captor
+    private ArgumentCaptor<Response> responseCaptor;
+
+    private RestconfDataServiceImpl dataService;
 
     @Before
     public void setUp() throws Exception {
         doReturn(Set.of()).when(queryParamenters).entrySet();
         doReturn(queryParamenters).when(uriInfo).getQueryParameters();
 
-        buildPlayerCont = Builders.containerBuilder()
-                .withNodeIdentifier(new NodeIdentifier(PLAYER_QNAME))
-                .withChild(GAP_LEAF)
-                .build();
-
-        buildLibraryCont = Builders.containerBuilder()
-                .withNodeIdentifier(new NodeIdentifier(LIBRARY_QNAME))
-                .build();
-
-        buildPlaylistList = Builders.mapBuilder()
-                .withNodeIdentifier(new NodeIdentifier(PLAYLIST_QNAME))
-                .build();
-
-        buildBaseCont = Builders.containerBuilder()
-                .withNodeIdentifier(new NodeIdentifier(JUKEBOX_QNAME))
-                .withChild(buildPlayerCont)
-                .build();
-
-        // config contains one child the same as in operational and one additional
-        buildBaseContConfig = Builders.containerBuilder()
-                .withNodeIdentifier(new NodeIdentifier(JUKEBOX_QNAME))
-                .withChild(buildPlayerCont)
-                .withChild(buildLibraryCont)
-                .build();
-
-        // operational contains one child the same as in config and one additional
-        buildBaseContOperational = Builders.containerBuilder()
-                .withNodeIdentifier(new NodeIdentifier(JUKEBOX_QNAME))
-                .withChild(buildPlayerCont)
-                .withChild(buildPlaylistList)
-                .build();
-
         doReturn(CommitInfo.emptyFluentFuture()).when(readWrite).commit();
 
-        DOMDataBroker mockDataBroker = mock(DOMDataBroker.class);
-        doReturn(read).when(mockDataBroker).newReadOnlyTransaction();
-        doReturn(readWrite).when(mockDataBroker).newReadWriteTransaction();
+        final var dataBroker = mock(DOMDataBroker.class);
+        doReturn(read).when(dataBroker).newReadOnlyTransaction();
+        doReturn(readWrite).when(dataBroker).newReadWriteTransaction();
 
-        dataService = new RestconfDataServiceImpl(() -> DatabindContext.ofModel(JUKEBOX_SCHEMA), mockDataBroker,
-                mountPointService, delegRestconfSubscrService, actionService, new StreamsConfiguration(0, 1, 0, false));
+        final DatabindProvider databindProvider = () -> DatabindContext.ofModel(JUKEBOX_SCHEMA);
+        dataService = new RestconfDataServiceImpl(databindProvider,
+            new MdsalRestconfServer(databindProvider, dataBroker, rpcService, actionService, mountPointService));
         doReturn(Optional.of(mountPoint)).when(mountPointService)
                 .getMountPoint(any(YangInstanceIdentifier.class));
         doReturn(Optional.of(FixedDOMSchemaService.of(JUKEBOX_SCHEMA))).when(mountPoint)
                 .getService(DOMSchemaService.class);
         doReturn(Optional.of(mountDataBroker)).when(mountPoint).getService(DOMDataBroker.class);
+        doReturn(Optional.of(rpcService)).when(mountPoint).getService(DOMRpcService.class);
         doReturn(Optional.empty()).when(mountPoint).getService(NetconfDataTreeService.class);
         doReturn(read).when(mountDataBroker).newReadOnlyTransaction();
         doReturn(readWrite).when(mountDataBroker).newReadWriteTransaction();
@@ -171,34 +144,37 @@ public class RestconfDataServiceImplTest extends AbstractJukeboxTest {
     @Test
     public void testReadData() {
         doReturn(new MultivaluedHashMap<>()).when(uriInfo).getQueryParameters();
-        doReturn(immediateFluentFuture(Optional.of(buildBaseCont))).when(read)
+        doReturn(immediateFluentFuture(Optional.of(EMPTY_JUKEBOX))).when(read)
                 .read(LogicalDatastoreType.CONFIGURATION, JUKEBOX_IID);
         doReturn(immediateFluentFuture(Optional.empty()))
                 .when(read).read(LogicalDatastoreType.OPERATIONAL, JUKEBOX_IID);
-        final Response response = dataService.readData("example-jukebox:jukebox", uriInfo);
-        assertNotNull(response);
+
+        doReturn(true).when(asyncResponse).resume(responseCaptor.capture());
+        dataService.dataGET("example-jukebox:jukebox", uriInfo, asyncResponse);
+        final var response = responseCaptor.getValue();
         assertEquals(200, response.getStatus());
-        assertEquals(buildBaseCont, ((NormalizedNodePayload) response.getEntity()).getData());
+        assertEquals(EMPTY_JUKEBOX, ((NormalizedNodePayload) response.getEntity()).data());
     }
 
     @Test
     public void testReadRootData() {
         doReturn(new MultivaluedHashMap<>()).when(uriInfo).getQueryParameters();
-        doReturn(immediateFluentFuture(Optional.of(wrapNodeByDataRootContainer(buildBaseContConfig))))
+        doReturn(immediateFluentFuture(Optional.of(wrapNodeByDataRootContainer(CONFIG_JUKEBOX))))
                 .when(read)
                 .read(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.of());
-        doReturn(immediateFluentFuture(Optional.of(wrapNodeByDataRootContainer(buildBaseContOperational))))
+        doReturn(immediateFluentFuture(Optional.of(wrapNodeByDataRootContainer(OPER_JUKEBOX))))
                 .when(read)
                 .read(LogicalDatastoreType.OPERATIONAL, YangInstanceIdentifier.of());
-        final Response response = dataService.readData(uriInfo);
-        assertNotNull(response);
+
+        doReturn(true).when(asyncResponse).resume(responseCaptor.capture());
+        dataService.dataGET(uriInfo, asyncResponse);
+        final var response = responseCaptor.getValue();
         assertEquals(200, response.getStatus());
 
-        final NormalizedNode data = ((NormalizedNodePayload) response.getEntity()).getData();
-        assertTrue(data instanceof ContainerNode);
-        final Collection<DataContainerChild> rootNodes = ((ContainerNode) data).body();
+        final var data = assertInstanceOf(ContainerNode.class, ((NormalizedNodePayload) response.getEntity()).data());
+        final var rootNodes = data.body();
         assertEquals(1, rootNodes.size());
-        final Collection<DataContainerChild> allDataChildren = ((ContainerNode) rootNodes.iterator().next()).body();
+        final var allDataChildren = assertInstanceOf(ContainerNode.class, rootNodes.iterator().next()).body();
         assertEquals(3, allDataChildren.size());
     }
 
@@ -216,24 +192,22 @@ public class RestconfDataServiceImplTest extends AbstractJukeboxTest {
     @Test
     public void testReadDataMountPoint() {
         doReturn(new MultivaluedHashMap<>()).when(uriInfo).getQueryParameters();
-        doReturn(immediateFluentFuture(Optional.of(buildBaseContConfig))).when(read)
+        doReturn(immediateFluentFuture(Optional.of(CONFIG_JUKEBOX))).when(read)
                 .read(LogicalDatastoreType.CONFIGURATION, JUKEBOX_IID);
-        doReturn(immediateFluentFuture(Optional.of(buildBaseContOperational))).when(read)
+        doReturn(immediateFluentFuture(Optional.of(OPER_JUKEBOX))).when(read)
                 .read(LogicalDatastoreType.OPERATIONAL, JUKEBOX_IID);
 
-        final Response response = dataService.readData(
-                "example-jukebox:jukebox/yang-ext:mount/example-jukebox:jukebox", uriInfo);
-
-        assertNotNull(response);
+        doReturn(true).when(asyncResponse).resume(responseCaptor.capture());
+        dataService.dataGET("example-jukebox:jukebox/yang-ext:mount/example-jukebox:jukebox", uriInfo, asyncResponse);
+        final var response = responseCaptor.getValue();
         assertEquals(200, response.getStatus());
 
         // response must contain all child nodes from config and operational containers merged in one container
-        final NormalizedNode data = ((NormalizedNodePayload) response.getEntity()).getData();
-        assertTrue(data instanceof ContainerNode);
-        assertEquals(3, ((ContainerNode) data).size());
-        assertNotNull(((ContainerNode) data).childByArg(buildPlayerCont.name()));
-        assertNotNull(((ContainerNode) data).childByArg(buildLibraryCont.name()));
-        assertNotNull(((ContainerNode) data).childByArg(buildPlaylistList.name()));
+        final var data = assertInstanceOf(ContainerNode.class, ((NormalizedNodePayload) response.getEntity()).data());
+        assertEquals(3, data.size());
+        assertNotNull(data.childByArg(CONT_PLAYER.name()));
+        assertNotNull(data.childByArg(LIBRARY_NID));
+        assertNotNull(data.childByArg(PLAYLIST_NID));
     }
 
     @Test
@@ -244,8 +218,11 @@ public class RestconfDataServiceImplTest extends AbstractJukeboxTest {
         doReturn(immediateFluentFuture(Optional.empty()))
                 .when(read).read(LogicalDatastoreType.OPERATIONAL, JUKEBOX_IID);
 
-        final var errors = assertThrows(RestconfDocumentedException.class,
-            () -> dataService.readData("example-jukebox:jukebox", uriInfo)).getErrors();
+        final var rdeCaptor = ArgumentCaptor.forClass(RestconfDocumentedException.class);
+        doReturn(true).when(asyncResponse).resume(rdeCaptor.capture());
+        dataService.dataGET("example-jukebox:jukebox", uriInfo, asyncResponse);
+
+        final var errors = rdeCaptor.getValue().getErrors();
         assertEquals(1, errors.size());
         final var error = errors.get(0);
         assertEquals(ErrorType.PROTOCOL, error.getErrorType());
@@ -263,23 +240,23 @@ public class RestconfDataServiceImplTest extends AbstractJukeboxTest {
         parameters.put("content", List.of("config"));
 
         doReturn(parameters).when(uriInfo).getQueryParameters();
-        doReturn(immediateFluentFuture(Optional.of(buildBaseContConfig))).when(read)
+        doReturn(immediateFluentFuture(Optional.of(CONFIG_JUKEBOX))).when(read)
                 .read(LogicalDatastoreType.CONFIGURATION, JUKEBOX_IID);
 
-        final Response response = dataService.readData("example-jukebox:jukebox", uriInfo);
-
-        assertNotNull(response);
+        doReturn(true).when(asyncResponse).resume(responseCaptor.capture());
+        dataService.dataGET("example-jukebox:jukebox", uriInfo, asyncResponse);
+        final var response = responseCaptor.getValue();
         assertEquals(200, response.getStatus());
 
         // response must contain only config data
-        final NormalizedNode data = ((NormalizedNodePayload) response.getEntity()).getData();
+        final var data = assertInstanceOf(ContainerNode.class, ((NormalizedNodePayload) response.getEntity()).data());
 
         // config data present
-        assertNotNull(((ContainerNode) data).childByArg(buildPlayerCont.name()));
-        assertNotNull(((ContainerNode) data).childByArg(buildLibraryCont.name()));
+        assertNotNull(data.childByArg(CONT_PLAYER.name()));
+        assertNotNull(data.childByArg(LIBRARY_NID));
 
         // state data absent
-        assertNull(((ContainerNode) data).childByArg(buildPlaylistList.name()));
+        assertNull(data.childByArg(PLAYLIST_NID));
     }
 
     /**
@@ -291,64 +268,83 @@ public class RestconfDataServiceImplTest extends AbstractJukeboxTest {
         parameters.put("content", List.of("nonconfig"));
 
         doReturn(parameters).when(uriInfo).getQueryParameters();
-        doReturn(immediateFluentFuture(Optional.of(buildBaseContOperational))).when(read)
+        doReturn(immediateFluentFuture(Optional.of(OPER_JUKEBOX))).when(read)
                 .read(LogicalDatastoreType.OPERATIONAL, JUKEBOX_IID);
 
-        final Response response = dataService.readData("example-jukebox:jukebox", uriInfo);
+        doReturn(true).when(asyncResponse).resume(responseCaptor.capture());
+        dataService.dataGET("example-jukebox:jukebox", uriInfo, asyncResponse);
+        final var response = responseCaptor.getValue();
 
-        assertNotNull(response);
         assertEquals(200, response.getStatus());
 
         // response must contain only operational data
-        final NormalizedNode data = ((NormalizedNodePayload) response.getEntity()).getData();
+        final var data = assertInstanceOf(ContainerNode.class, ((NormalizedNodePayload) response.getEntity()).data());
 
         // state data present
-        assertNotNull(((ContainerNode) data).childByArg(buildPlayerCont.name()));
-        assertNotNull(((ContainerNode) data).childByArg(buildPlaylistList.name()));
+        assertNotNull(data.childByArg(CONT_PLAYER.name()));
+        assertNotNull(data.childByArg(PLAYLIST_NID));
 
         // config data absent
-        assertNull(((ContainerNode) data).childByArg(buildLibraryCont.name()));
+        assertNull(data.childByArg(LIBRARY_NID));
     }
 
     @Test
     public void testPutData() {
-        final InstanceIdentifierContext iidContext = InstanceIdentifierContext.ofLocalPath(JUKEBOX_SCHEMA, JUKEBOX_IID);
-        final NormalizedNodePayload payload = NormalizedNodePayload.of(iidContext, buildBaseCont);
-
         doReturn(immediateTrueFluentFuture()).when(read)
                 .exists(LogicalDatastoreType.CONFIGURATION, JUKEBOX_IID);
-        doNothing().when(readWrite).put(LogicalDatastoreType.CONFIGURATION, JUKEBOX_IID, payload.getData());
-        final Response response = dataService.putData(null, payload, uriInfo);
-        assertNotNull(response);
+        doNothing().when(readWrite).put(LogicalDatastoreType.CONFIGURATION, JUKEBOX_IID, EMPTY_JUKEBOX);
+
+        doReturn(true).when(asyncResponse).resume(responseCaptor.capture());
+        dataService.putDataJSON("example-jukebox:jukebox", uriInfo, stringInputStream("""
+            {
+              "example-jukebox:jukebox" : {
+                 "player": {
+                   "gap": "0.2"
+                 }
+              }
+            }"""), asyncResponse);
+        final var response = responseCaptor.getValue();
         assertEquals(Response.Status.NO_CONTENT.getStatusCode(), response.getStatus());
     }
 
     @Test
     public void testPutDataWithMountPoint() {
-        final InstanceIdentifierContext iidContext =
-            InstanceIdentifierContext.ofMountPointPath(mountPoint, JUKEBOX_SCHEMA, JUKEBOX_IID);
-        final NormalizedNodePayload payload = NormalizedNodePayload.of(iidContext, buildBaseCont);
-
         doReturn(immediateTrueFluentFuture()).when(read)
                 .exists(LogicalDatastoreType.CONFIGURATION, JUKEBOX_IID);
-        doNothing().when(readWrite).put(LogicalDatastoreType.CONFIGURATION, JUKEBOX_IID, payload.getData());
-        final Response response = dataService.putData(null, payload, uriInfo);
-        assertNotNull(response);
+        doNothing().when(readWrite).put(LogicalDatastoreType.CONFIGURATION, JUKEBOX_IID, EMPTY_JUKEBOX);
+
+        doReturn(true).when(asyncResponse).resume(responseCaptor.capture());
+        dataService.putDataXML("example-jukebox:jukebox/yang-ext:mount/example-jukebox:jukebox",
+            uriInfo, stringInputStream("""
+                <jukebox xmlns="http://example.com/ns/example-jukebox">
+                  <player>
+                    <gap>0.2</gap>
+                  </player>
+                </jukebox>"""), asyncResponse);
+        final var response = responseCaptor.getValue();
         assertEquals(Response.Status.NO_CONTENT.getStatusCode(), response.getStatus());
     }
 
+    private static InputStream stringInputStream(final String str) {
+        return new ByteArrayInputStream(str.getBytes(StandardCharsets.UTF_8));
+    }
+
     @Test
     public void testPostData() {
         doReturn(new MultivaluedHashMap<>()).when(uriInfo).getQueryParameters();
-        final var node = JUKEBOX_IID.node(BAND_ENTRY.name());
-        doReturn(immediateFalseFluentFuture()).when(readWrite).exists(LogicalDatastoreType.CONFIGURATION, node);
-        doNothing().when(readWrite).put(LogicalDatastoreType.CONFIGURATION, node, BAND_ENTRY);
+        doReturn(immediateFalseFluentFuture()).when(readWrite).exists(LogicalDatastoreType.CONFIGURATION, JUKEBOX_IID);
+        doNothing().when(readWrite).put(LogicalDatastoreType.CONFIGURATION, JUKEBOX_IID,
+            Builders.containerBuilder().withNodeIdentifier(new NodeIdentifier(JUKEBOX_QNAME)).build());
         doReturn(UriBuilder.fromUri("http://localhost:8181/rests/")).when(uriInfo).getBaseUriBuilder();
 
-        final var response = dataService.postData(NormalizedNodePayload.of(
-            InstanceIdentifierContext.ofLocalPath(JUKEBOX_SCHEMA, JUKEBOX_IID),
-            Builders.mapBuilder().withNodeIdentifier(new NodeIdentifier(PLAYLIST_QNAME)).withChild(BAND_ENTRY).build()),
-            uriInfo);
+        final var captor = ArgumentCaptor.forClass(Response.class);
+        doReturn(true).when(asyncResponse).resume(captor.capture());
+        dataService.postDataJSON(stringInputStream("""
+            {
+              "example-jukebox:jukebox" : {
+              }
+            }"""), uriInfo, asyncResponse);
+        final var response = captor.getValue();
         assertEquals(201, response.getStatus());
         assertEquals(URI.create("http://localhost:8181/rests/data/example-jukebox:jukebox"), response.getLocation());
     }
@@ -361,10 +357,16 @@ public class RestconfDataServiceImplTest extends AbstractJukeboxTest {
         doNothing().when(readWrite).put(LogicalDatastoreType.CONFIGURATION, node, BAND_ENTRY);
         doReturn(UriBuilder.fromUri("http://localhost:8181/rests/")).when(uriInfo).getBaseUriBuilder();
 
-        final var response = dataService.postData(NormalizedNodePayload.of(
-            InstanceIdentifierContext.ofLocalPath(JUKEBOX_SCHEMA, PLAYLIST_IID),
-            Builders.mapBuilder().withNodeIdentifier(new NodeIdentifier(PLAYLIST_QNAME)).withChild(BAND_ENTRY).build()),
-            uriInfo);
+        final var captor = ArgumentCaptor.forClass(Response.class);
+        doReturn(true).when(asyncResponse).resume(captor.capture());
+        dataService.postDataJSON("example-jukebox:jukebox", stringInputStream("""
+            {
+              "example-jukebox:playlist" : {
+                "name" : "name of band",
+                "description" : "band description"
+              }
+            }"""), uriInfo, asyncResponse);
+        final var response = captor.getValue();
         assertEquals(201, response.getStatus());
         assertEquals(URI.create("http://localhost:8181/rests/data/example-jukebox:jukebox/playlist=name%20of%20band"),
             response.getLocation());
@@ -414,18 +416,22 @@ public class RestconfDataServiceImplTest extends AbstractJukeboxTest {
 
     @Test
     public void testPatchData() {
-        final InstanceIdentifierContext iidContext = InstanceIdentifierContext.ofLocalPath(JUKEBOX_SCHEMA, JUKEBOX_IID);
-        final PatchContext patch = new PatchContext(iidContext, List.of(
-            new PatchEntity("create data", Operation.Create, JUKEBOX_IID, buildBaseCont),
-            new PatchEntity("replace data", Operation.Replace, JUKEBOX_IID, buildBaseCont),
-            new PatchEntity("delete data", Operation.Delete, GAP_IID)), "test patch id");
+        final var patch = new PatchContext("test patch id", List.of(
+            new PatchEntity("create data", Operation.Create, JUKEBOX_IID, EMPTY_JUKEBOX),
+            new PatchEntity("replace data", Operation.Replace, JUKEBOX_IID, EMPTY_JUKEBOX),
+            new PatchEntity("delete data", Operation.Delete, GAP_IID)));
 
         doNothing().when(readWrite).delete(LogicalDatastoreType.CONFIGURATION, GAP_IID);
         doReturn(immediateFalseFluentFuture())
                 .when(readWrite).exists(LogicalDatastoreType.CONFIGURATION, JUKEBOX_IID);
         doReturn(immediateTrueFluentFuture())
                 .when(readWrite).exists(LogicalDatastoreType.CONFIGURATION, GAP_IID);
-        final PatchStatusContext status = dataService.yangPatchData(patch);
+        doReturn(true).when(asyncResponse).resume(responseCaptor.capture());
+        dataService.yangPatchData(JUKEBOX_SCHEMA, patch, null, asyncResponse);
+        final var response = responseCaptor.getValue();
+        assertEquals(200, response.getStatus());
+        final var status = assertInstanceOf(PatchStatusContext.class, response.getEntity());
+
         assertTrue(status.ok());
         assertEquals(3, status.editCollection().size());
         assertEquals("replace data", status.editCollection().get(1).getEditId());
@@ -433,19 +439,22 @@ public class RestconfDataServiceImplTest extends AbstractJukeboxTest {
 
     @Test
     public void testPatchDataMountPoint() throws Exception {
-        final InstanceIdentifierContext iidContext = InstanceIdentifierContext.ofMountPointPath(mountPoint,
-            JUKEBOX_SCHEMA, JUKEBOX_IID);
-        final PatchContext patch = new PatchContext(iidContext, List.of(
-            new PatchEntity("create data", Operation.Create, JUKEBOX_IID, buildBaseCont),
-            new PatchEntity("replace data", Operation.Replace, JUKEBOX_IID, buildBaseCont),
-            new PatchEntity("delete data", Operation.Delete, GAP_IID)), "test patch id");
+        final var patch = new PatchContext("test patch id", List.of(
+            new PatchEntity("create data", Operation.Create, JUKEBOX_IID, EMPTY_JUKEBOX),
+            new PatchEntity("replace data", Operation.Replace, JUKEBOX_IID, EMPTY_JUKEBOX),
+            new PatchEntity("delete data", Operation.Delete, GAP_IID)));
 
         doNothing().when(readWrite).delete(LogicalDatastoreType.CONFIGURATION, GAP_IID);
         doReturn(immediateFalseFluentFuture())
                 .when(readWrite).exists(LogicalDatastoreType.CONFIGURATION, JUKEBOX_IID);
         doReturn(immediateTrueFluentFuture()).when(readWrite).exists(LogicalDatastoreType.CONFIGURATION, GAP_IID);
 
-        final PatchStatusContext status = dataService.yangPatchData(patch);
+        doReturn(true).when(asyncResponse).resume(responseCaptor.capture());
+        dataService.yangPatchData(JUKEBOX_SCHEMA, patch, mountPoint, asyncResponse);
+        final var response = responseCaptor.getValue();
+        assertEquals(200, response.getStatus());
+        final var status = assertInstanceOf(PatchStatusContext.class, response.getEntity());
+
         assertTrue(status.ok());
         assertEquals(3, status.editCollection().size());
         assertNull(status.globalErrors());
@@ -453,11 +462,10 @@ public class RestconfDataServiceImplTest extends AbstractJukeboxTest {
 
     @Test
     public void testPatchDataDeleteNotExist() {
-        final InstanceIdentifierContext iidContext = InstanceIdentifierContext.ofLocalPath(JUKEBOX_SCHEMA, JUKEBOX_IID);
-        final PatchContext patch = new PatchContext(iidContext, List.of(
-            new PatchEntity("create data", Operation.Create, JUKEBOX_IID, buildBaseCont),
+        final var patch = new PatchContext("test patch id", List.of(
+            new PatchEntity("create data", Operation.Create, JUKEBOX_IID, EMPTY_JUKEBOX),
             new PatchEntity("remove data", Operation.Remove, GAP_IID),
-            new PatchEntity("delete data", Operation.Delete, GAP_IID)), "test patch id");
+            new PatchEntity("delete data", Operation.Delete, GAP_IID)));
 
         doNothing().when(readWrite).delete(LogicalDatastoreType.CONFIGURATION, GAP_IID);
         doReturn(immediateFalseFluentFuture())
@@ -465,7 +473,12 @@ public class RestconfDataServiceImplTest extends AbstractJukeboxTest {
         doReturn(immediateFalseFluentFuture())
                 .when(readWrite).exists(LogicalDatastoreType.CONFIGURATION, GAP_IID);
         doReturn(true).when(readWrite).cancel();
-        final PatchStatusContext status = dataService.yangPatchData(patch);
+
+        doReturn(true).when(asyncResponse).resume(responseCaptor.capture());
+        dataService.yangPatchData(JUKEBOX_SCHEMA, patch, null, asyncResponse);
+        final var response = responseCaptor.getValue();
+        assertEquals(409, response.getStatus());
+        final var status = assertInstanceOf(PatchStatusContext.class, response.getEntity());
 
         assertFalse(status.ok());
         assertEquals(3, status.editCollection().size());
@@ -476,59 +489,4 @@ public class RestconfDataServiceImplTest extends AbstractJukeboxTest {
         final String errorMessage = status.editCollection().get(2).getEditErrors().get(0).getErrorMessage();
         assertEquals("Data does not exist", errorMessage);
     }
-
-    @Test
-    public void testGetRestconfStrategy() {
-        RestconfStrategy restconfStrategy = dataService.getRestconfStrategy(mountPoint);
-        assertTrue(restconfStrategy instanceof MdsalRestconfStrategy);
-
-        doReturn(Optional.of(netconfService)).when(mountPoint).getService(NetconfDataTreeService.class);
-        restconfStrategy = dataService.getRestconfStrategy(mountPoint);
-        assertTrue(restconfStrategy instanceof NetconfRestconfStrategy);
-    }
-
-    @Test
-    public void testValidInputData() {
-        RestconfDataServiceImpl.validInputData(true, NormalizedNodePayload.of(
-            InstanceIdentifierContext.ofLocalPath(JUKEBOX_SCHEMA, GAP_IID), GAP_LEAF));
-    }
-
-    @Test
-    public void testValidTopLevelNodeName() {
-        RestconfDataServiceImpl.validTopLevelNodeName(GAP_IID, NormalizedNodePayload.of(
-            InstanceIdentifierContext.ofLocalPath(JUKEBOX_SCHEMA, GAP_IID), GAP_LEAF));
-        RestconfDataServiceImpl.validTopLevelNodeName(JUKEBOX_IID, NormalizedNodePayload.of(
-            InstanceIdentifierContext.ofLocalPath(JUKEBOX_SCHEMA, JUKEBOX_IID), EMPTY_JUKEBOX));
-    }
-
-    @Test
-    public void testValidTopLevelNodeNamePathEmpty() {
-        final var iidContext = InstanceIdentifierContext.ofLocalPath(JUKEBOX_SCHEMA, GAP_IID);
-        final var payload = NormalizedNodePayload.of(iidContext, GAP_LEAF);
-
-        // FIXME: more asserts
-        assertThrows(RestconfDocumentedException.class,
-            () -> RestconfDataServiceImpl.validTopLevelNodeName(YangInstanceIdentifier.of(), payload));
-    }
-
-    @Test
-    public void testValidTopLevelNodeNameWrongTopIdentifier() {
-        final var iidContext = InstanceIdentifierContext.ofLocalPath(JUKEBOX_SCHEMA, GAP_IID);
-        final var payload = NormalizedNodePayload.of(iidContext, GAP_LEAF);
-
-        // FIXME: more asserts
-        assertThrows(RestconfDocumentedException.class,
-            () -> RestconfDataServiceImpl.validTopLevelNodeName(GAP_IID.getAncestor(1), payload));
-    }
-
-    @Test
-    public void testValidateListKeysEqualityInPayloadAndUri() {
-        final var iidContext = InstanceIdentifierContext.ofLocalPath(JUKEBOX_SCHEMA, YangInstanceIdentifier.builder()
-            .node(JUKEBOX_QNAME)
-            .node(PLAYLIST_QNAME)
-            .nodeWithKey(PLAYLIST_QNAME, NAME_QNAME, "name of band")
-            .build());
-        final NormalizedNodePayload payload = NormalizedNodePayload.of(iidContext, BAND_ENTRY);
-        RestconfDataServiceImpl.validateListKeysEqualityInPayloadAndUri(payload);
-    }
 }