X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=restconf%2Frestconf-nb%2Fsrc%2Ftest%2Fjava%2Forg%2Fopendaylight%2Frestconf%2Fnb%2Frfc8040%2Frests%2Fservices%2Fimpl%2FRestconfDataServiceImplTest.java;h=be6b7e861afc21644f05bbb9480f9475790f8c82;hb=8616c9655db145125c23ca8e66b5f27745126458;hp=cbb2bb342d256683c67ac0b78b4b6b9fe3f36e23;hpb=49aecc8858d72f0aed9ba44015ae55a71620e13b;p=netconf.git diff --git a/restconf/restconf-nb/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/RestconfDataServiceImplTest.java b/restconf/restconf-nb/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/RestconfDataServiceImplTest.java index cbb2bb342d..be6b7e861a 100644 --- a/restconf/restconf-nb/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/RestconfDataServiceImplTest.java +++ b/restconf/restconf-nb/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/RestconfDataServiceImplTest.java @@ -7,28 +7,30 @@ */ 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.assertThrows; +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; import static org.mockito.Mockito.mock; -import static org.opendaylight.restconf.common.patch.PatchEditOperation.CREATE; -import static org.opendaylight.restconf.common.patch.PatchEditOperation.DELETE; -import static org.opendaylight.restconf.common.patch.PatchEditOperation.REMOVE; -import static org.opendaylight.restconf.common.patch.PatchEditOperation.REPLACE; import static org.opendaylight.yangtools.util.concurrent.FluentFutures.immediateFalseFluentFuture; import static org.opendaylight.yangtools.util.concurrent.FluentFutures.immediateFluentFuture; import static org.opendaylight.yangtools.util.concurrent.FluentFutures.immediateTrueFluentFuture; +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.net.URI; +import java.nio.charset.StandardCharsets; import java.util.Collection; import java.util.List; import java.util.Optional; import java.util.Set; +import javax.ws.rs.container.AsyncResponse; import javax.ws.rs.core.MultivaluedHashMap; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.Response; @@ -37,6 +39,8 @@ import javax.ws.rs.core.UriInfo; 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; @@ -45,58 +49,49 @@ 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; -import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates; import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild; -import org.opendaylight.yangtools.yang.data.api.schema.LeafNode; -import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode; -import org.opendaylight.yangtools.yang.data.api.schema.MapNode; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; import org.opendaylight.yangtools.yang.data.impl.schema.Builders; -import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes; 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 YangInstanceIdentifier iidBase; - private RestconfDataServiceImpl dataService; - private QName baseQName; - private QName containerPlayerQname; - private QName leafQname; - 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 @@ -104,8 +99,6 @@ public class RestconfDataServiceImplTest extends AbstractJukeboxTest { @Mock private DOMDataTreeReadTransaction read; @Mock - private DOMDataTreeWriteTransaction write; - @Mock private DOMMountPointService mountPointService; @Mock private DOMMountPoint mountPoint; @@ -116,76 +109,36 @@ public class RestconfDataServiceImplTest extends AbstractJukeboxTest { @Mock private DOMActionService actionService; @Mock - private RestconfStreamsSubscriptionService delegRestconfSubscrService; + private DOMRpcService rpcService; @Mock private MultivaluedMap queryParamenters; + @Mock + private AsyncResponse asyncResponse; + @Captor + private ArgumentCaptor responseCaptor; + + private RestconfDataServiceImpl dataService; @Before public void setUp() throws Exception { doReturn(Set.of()).when(queryParamenters).entrySet(); doReturn(queryParamenters).when(uriInfo).getQueryParameters(); - baseQName = QName.create("http://example.com/ns/example-jukebox", "2015-04-04", "jukebox"); - containerPlayerQname = QName.create(baseQName, "player"); - leafQname = QName.create(baseQName, "gap"); - - final QName containerLibraryQName = QName.create(baseQName, "library"); - final QName listPlaylistQName = QName.create(baseQName, "playlist"); - - final LeafNode buildLeaf = Builders.leafBuilder() - .withNodeIdentifier(new NodeIdentifier(leafQname)) - .withValue(0.2) - .build(); - - buildPlayerCont = Builders.containerBuilder() - .withNodeIdentifier(new NodeIdentifier(containerPlayerQname)) - .withChild(buildLeaf) - .build(); - - buildLibraryCont = Builders.containerBuilder() - .withNodeIdentifier(new NodeIdentifier(containerLibraryQName)) - .build(); - - buildPlaylistList = Builders.mapBuilder() - .withNodeIdentifier(new NodeIdentifier(listPlaylistQName)) - .build(); - - buildBaseCont = Builders.containerBuilder() - .withNodeIdentifier(new NodeIdentifier(baseQName)) - .withChild(buildPlayerCont) - .build(); - - // config contains one child the same as in operational and one additional - buildBaseContConfig = Builders.containerBuilder() - .withNodeIdentifier(new NodeIdentifier(baseQName)) - .withChild(buildPlayerCont) - .withChild(buildLibraryCont) - .build(); - - // operational contains one child the same as in config and one additional - buildBaseContOperational = Builders.containerBuilder() - .withNodeIdentifier(new NodeIdentifier(baseQName)) - .withChild(buildPlayerCont) - .withChild(buildPlaylistList) - .build(); - - iidBase = YangInstanceIdentifier.builder() - .node(baseQName) - .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, mountPointService), actionService); 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(); @@ -194,30 +147,30 @@ public class RestconfDataServiceImplTest extends AbstractJukeboxTest { @Test public void testReadData() { doReturn(new MultivaluedHashMap<>()).when(uriInfo).getQueryParameters(); - doReturn(immediateFluentFuture(Optional.of(buildBaseCont))).when(read) - .read(LogicalDatastoreType.CONFIGURATION, iidBase); + doReturn(immediateFluentFuture(Optional.of(EMPTY_JUKEBOX))).when(read) + .read(LogicalDatastoreType.CONFIGURATION, JUKEBOX_IID); doReturn(immediateFluentFuture(Optional.empty())) - .when(read).read(LogicalDatastoreType.OPERATIONAL, iidBase); + .when(read).read(LogicalDatastoreType.OPERATIONAL, JUKEBOX_IID); final Response response = dataService.readData("example-jukebox:jukebox", uriInfo); assertNotNull(response); 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); assertEquals(200, response.getStatus()); - final NormalizedNode data = ((NormalizedNodePayload) response.getEntity()).getData(); + final NormalizedNode data = ((NormalizedNodePayload) response.getEntity()).data(); assertTrue(data instanceof ContainerNode); final Collection rootNodes = ((ContainerNode) data).body(); assertEquals(1, rootNodes.size()); @@ -239,10 +192,10 @@ public class RestconfDataServiceImplTest extends AbstractJukeboxTest { @Test public void testReadDataMountPoint() { doReturn(new MultivaluedHashMap<>()).when(uriInfo).getQueryParameters(); - doReturn(immediateFluentFuture(Optional.of(buildBaseContConfig))).when(read) - .read(LogicalDatastoreType.CONFIGURATION, iidBase); - doReturn(immediateFluentFuture(Optional.of(buildBaseContOperational))).when(read) - .read(LogicalDatastoreType.OPERATIONAL, iidBase); + doReturn(immediateFluentFuture(Optional.of(CONFIG_JUKEBOX))).when(read) + .read(LogicalDatastoreType.CONFIGURATION, JUKEBOX_IID); + 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); @@ -251,21 +204,21 @@ public class RestconfDataServiceImplTest extends AbstractJukeboxTest { 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(); + final NormalizedNode data = ((NormalizedNodePayload) response.getEntity()).data(); 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())); + assertNotNull(((ContainerNode) data).childByArg(CONT_PLAYER.name())); + assertNotNull(((ContainerNode) data).childByArg(LIBRARY_NID)); + assertNotNull(((ContainerNode) data).childByArg(PLAYLIST_NID)); } @Test public void testReadDataNoData() { doReturn(new MultivaluedHashMap<>()).when(uriInfo).getQueryParameters(); doReturn(immediateFluentFuture(Optional.empty())) - .when(read).read(LogicalDatastoreType.CONFIGURATION, iidBase); + .when(read).read(LogicalDatastoreType.CONFIGURATION, JUKEBOX_IID); doReturn(immediateFluentFuture(Optional.empty())) - .when(read).read(LogicalDatastoreType.OPERATIONAL, iidBase); + .when(read).read(LogicalDatastoreType.OPERATIONAL, JUKEBOX_IID); final var errors = assertThrows(RestconfDocumentedException.class, () -> dataService.readData("example-jukebox:jukebox", uriInfo)).getErrors(); @@ -286,8 +239,8 @@ public class RestconfDataServiceImplTest extends AbstractJukeboxTest { parameters.put("content", List.of("config")); doReturn(parameters).when(uriInfo).getQueryParameters(); - doReturn(immediateFluentFuture(Optional.of(buildBaseContConfig))).when(read) - .read(LogicalDatastoreType.CONFIGURATION, iidBase); + doReturn(immediateFluentFuture(Optional.of(CONFIG_JUKEBOX))).when(read) + .read(LogicalDatastoreType.CONFIGURATION, JUKEBOX_IID); final Response response = dataService.readData("example-jukebox:jukebox", uriInfo); @@ -295,14 +248,14 @@ public class RestconfDataServiceImplTest extends AbstractJukeboxTest { assertEquals(200, response.getStatus()); // response must contain only config data - final NormalizedNode data = ((NormalizedNodePayload) response.getEntity()).getData(); + final NormalizedNode data = ((NormalizedNodePayload) response.getEntity()).data(); // config data present - assertNotNull(((ContainerNode) data).childByArg(buildPlayerCont.name())); - assertNotNull(((ContainerNode) data).childByArg(buildLibraryCont.name())); + assertNotNull(((ContainerNode) data).childByArg(CONT_PLAYER.name())); + assertNotNull(((ContainerNode) data).childByArg(LIBRARY_NID)); // state data absent - assertNull(((ContainerNode) data).childByArg(buildPlaylistList.name())); + assertNull(((ContainerNode) data).childByArg(PLAYLIST_NID)); } /** @@ -314,8 +267,8 @@ public class RestconfDataServiceImplTest extends AbstractJukeboxTest { parameters.put("content", List.of("nonconfig")); doReturn(parameters).when(uriInfo).getQueryParameters(); - doReturn(immediateFluentFuture(Optional.of(buildBaseContOperational))).when(read) - .read(LogicalDatastoreType.OPERATIONAL, iidBase); + doReturn(immediateFluentFuture(Optional.of(OPER_JUKEBOX))).when(read) + .read(LogicalDatastoreType.OPERATIONAL, JUKEBOX_IID); final Response response = dataService.readData("example-jukebox:jukebox", uriInfo); @@ -323,82 +276,125 @@ public class RestconfDataServiceImplTest extends AbstractJukeboxTest { assertEquals(200, response.getStatus()); // response must contain only operational data - final NormalizedNode data = ((NormalizedNodePayload) response.getEntity()).getData(); + final NormalizedNode data = ((NormalizedNodePayload) response.getEntity()).data(); // state data present - assertNotNull(((ContainerNode) data).childByArg(buildPlayerCont.name())); - assertNotNull(((ContainerNode) data).childByArg(buildPlaylistList.name())); + assertNotNull(((ContainerNode) data).childByArg(CONT_PLAYER.name())); + assertNotNull(((ContainerNode) data).childByArg(PLAYLIST_NID)); // config data absent - assertNull(((ContainerNode) data).childByArg(buildLibraryCont.name())); + assertNull(((ContainerNode) data).childByArg(LIBRARY_NID)); } @Test public void testPutData() { - final InstanceIdentifierContext iidContext = InstanceIdentifierContext.ofLocalPath(JUKEBOX_SCHEMA, iidBase); - final NormalizedNodePayload payload = NormalizedNodePayload.of(iidContext, buildBaseCont); - doReturn(immediateTrueFluentFuture()).when(read) - .exists(LogicalDatastoreType.CONFIGURATION, iidBase); - doNothing().when(readWrite).put(LogicalDatastoreType.CONFIGURATION, iidBase, payload.getData()); - final Response response = dataService.putData(null, payload, uriInfo); - assertNotNull(response); + .exists(LogicalDatastoreType.CONFIGURATION, JUKEBOX_IID); + 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, iidBase); - final NormalizedNodePayload payload = NormalizedNodePayload.of(iidContext, buildBaseCont); - doReturn(immediateTrueFluentFuture()).when(read) - .exists(LogicalDatastoreType.CONFIGURATION, iidBase); - doNothing().when(readWrite).put(LogicalDatastoreType.CONFIGURATION, iidBase, payload.getData()); - final Response response = dataService.putData(null, payload, uriInfo); - assertNotNull(response); + .exists(LogicalDatastoreType.CONFIGURATION, JUKEBOX_IID); + 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(""" + + + 0.2 + + """), 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() { - final QName listQname = QName.create(baseQName, "playlist"); - final QName listKeyQname = QName.create(baseQName, "name"); - final NodeIdentifierWithPredicates nodeWithKey = - NodeIdentifierWithPredicates.of(listQname, listKeyQname, "name of band"); + doReturn(new MultivaluedHashMap<>()).when(uriInfo).getQueryParameters(); + 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 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()); + } + @Test + public void testPostMapEntryData() { doReturn(new MultivaluedHashMap<>()).when(uriInfo).getQueryParameters(); - final InstanceIdentifierContext iidContext = InstanceIdentifierContext.ofLocalPath(JUKEBOX_SCHEMA, iidBase); - final NormalizedNodePayload payload = NormalizedNodePayload.of(iidContext, Builders.mapBuilder() - .withNodeIdentifier(new NodeIdentifier(listQname)) - .withChild(Builders.mapEntryBuilder() - .withNodeIdentifier(nodeWithKey) - .withChild(ImmutableNodes.leafNode(QName.create(baseQName, "name"), "name of band")) - .withChild(ImmutableNodes.leafNode(QName.create(baseQName, "description"), "band description")) - .build()) - .build()); - final MapNode data = (MapNode) payload.getData(); - final MapEntryNode entryNode = data.body().iterator().next(); - final NodeIdentifierWithPredicates identifier = entryNode.name(); - final YangInstanceIdentifier node = - payload.getInstanceIdentifierContext().getInstanceIdentifier().node(identifier); - doReturn(immediateFalseFluentFuture()) - .when(readWrite).exists(LogicalDatastoreType.CONFIGURATION, node); - doNothing().when(readWrite).put(LogicalDatastoreType.CONFIGURATION, node, entryNode); + final var node = PLAYLIST_IID.node(BAND_ENTRY.name()); + doReturn(immediateFalseFluentFuture()).when(readWrite).exists(LogicalDatastoreType.CONFIGURATION, node); + doNothing().when(readWrite).put(LogicalDatastoreType.CONFIGURATION, node, BAND_ENTRY); doReturn(UriBuilder.fromUri("http://localhost:8181/rests/")).when(uriInfo).getBaseUriBuilder(); - final Response response = dataService.postData(null, payload, 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()); } @Test public void testDeleteData() { - doNothing().when(readWrite).delete(LogicalDatastoreType.CONFIGURATION, iidBase); + doNothing().when(readWrite).delete(LogicalDatastoreType.CONFIGURATION, JUKEBOX_IID); doReturn(immediateTrueFluentFuture()) - .when(readWrite).exists(LogicalDatastoreType.CONFIGURATION, iidBase); - final Response response = dataService.deleteData("example-jukebox:jukebox"); - assertNotNull(response); - assertEquals(Response.Status.NO_CONTENT.getStatusCode(), response.getStatus()); + .when(readWrite).exists(LogicalDatastoreType.CONFIGURATION, JUKEBOX_IID); + final var captor = ArgumentCaptor.forClass(Response.class); + doReturn(true).when(asyncResponse).resume(captor.capture()); + dataService.deleteData("example-jukebox:jukebox", asyncResponse); + + assertEquals(204, captor.getValue().getStatus()); + } + + @Test + public void testDeleteDataNotExisting() { + doReturn(immediateFalseFluentFuture()) + .when(readWrite).exists(LogicalDatastoreType.CONFIGURATION, JUKEBOX_IID); + final var captor = ArgumentCaptor.forClass(RestconfDocumentedException.class); + doReturn(true).when(asyncResponse).resume(captor.capture()); + dataService.deleteData("example-jukebox:jukebox", asyncResponse); + + final var errors = captor.getValue().getErrors(); + assertEquals(1, errors.size()); + final var error = errors.get(0); + assertEquals(ErrorType.PROTOCOL, error.getErrorType()); + assertEquals(ErrorTag.DATA_MISSING, error.getErrorTag()); } /** @@ -406,99 +402,89 @@ public class RestconfDataServiceImplTest extends AbstractJukeboxTest { */ @Test public void testDeleteDataMountPoint() { - doNothing().when(readWrite).delete(LogicalDatastoreType.CONFIGURATION, iidBase); + doNothing().when(readWrite).delete(LogicalDatastoreType.CONFIGURATION, JUKEBOX_IID); doReturn(immediateTrueFluentFuture()) - .when(readWrite).exists(LogicalDatastoreType.CONFIGURATION, iidBase); - final Response response = - dataService.deleteData("example-jukebox:jukebox/yang-ext:mount/example-jukebox:jukebox"); - assertNotNull(response); - assertEquals(Response.Status.NO_CONTENT.getStatusCode(), response.getStatus()); + .when(readWrite).exists(LogicalDatastoreType.CONFIGURATION, JUKEBOX_IID); + final var captor = ArgumentCaptor.forClass(Response.class); + doReturn(true).when(asyncResponse).resume(captor.capture()); + dataService.deleteData("example-jukebox:jukebox/yang-ext:mount/example-jukebox:jukebox", asyncResponse); + + assertEquals(204, captor.getValue().getStatus()); } @Test public void testPatchData() { - final InstanceIdentifierContext iidContext = InstanceIdentifierContext.ofLocalPath(JUKEBOX_SCHEMA, iidBase); - final YangInstanceIdentifier iidleaf = YangInstanceIdentifier.builder(iidBase) - .node(containerPlayerQname) - .node(leafQname) - .build(); - final PatchContext patch = new PatchContext(iidContext, List.of( - new PatchEntity("create data", CREATE, iidBase, buildBaseCont), - new PatchEntity("replace data", REPLACE, iidBase, buildBaseCont), - new PatchEntity("delete data", DELETE, iidleaf)), "test patch id"); - - doNothing().when(readWrite).delete(LogicalDatastoreType.CONFIGURATION, iidleaf); + 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, iidBase); + .when(readWrite).exists(LogicalDatastoreType.CONFIGURATION, JUKEBOX_IID); doReturn(immediateTrueFluentFuture()) - .when(readWrite).exists(LogicalDatastoreType.CONFIGURATION, iidleaf); - final PatchStatusContext status = dataService.patchData(patch, uriInfo); - assertTrue(status.isOk()); - assertEquals(3, status.getEditCollection().size()); - assertEquals("replace data", status.getEditCollection().get(1).getEditId()); + .when(readWrite).exists(LogicalDatastoreType.CONFIGURATION, GAP_IID); + 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()); } @Test public void testPatchDataMountPoint() throws Exception { - final InstanceIdentifierContext iidContext = InstanceIdentifierContext.ofMountPointPath(mountPoint, - JUKEBOX_SCHEMA, iidBase); - final YangInstanceIdentifier iidleaf = YangInstanceIdentifier.builder(iidBase) - .node(containerPlayerQname) - .node(leafQname) - .build(); - final PatchContext patch = new PatchContext(iidContext, List.of( - new PatchEntity("create data", CREATE, iidBase, buildBaseCont), - new PatchEntity("replace data", REPLACE, iidBase, buildBaseCont), - new PatchEntity("delete data", DELETE, iidleaf)), "test patch id"); - - doNothing().when(readWrite).delete(LogicalDatastoreType.CONFIGURATION, iidleaf); + 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, iidBase); - doReturn(immediateTrueFluentFuture()).when(readWrite).exists(LogicalDatastoreType.CONFIGURATION, iidleaf); + .when(readWrite).exists(LogicalDatastoreType.CONFIGURATION, JUKEBOX_IID); + doReturn(immediateTrueFluentFuture()).when(readWrite).exists(LogicalDatastoreType.CONFIGURATION, GAP_IID); + + 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()); - final PatchStatusContext status = dataService.patchData(patch, uriInfo); - assertTrue(status.isOk()); - assertEquals(3, status.getEditCollection().size()); - assertNull(status.getGlobalErrors()); + assertTrue(status.ok()); + assertEquals(3, status.editCollection().size()); + assertNull(status.globalErrors()); } @Test public void testPatchDataDeleteNotExist() { - final InstanceIdentifierContext iidContext = InstanceIdentifierContext.ofLocalPath(JUKEBOX_SCHEMA, iidBase); - final YangInstanceIdentifier iidleaf = YangInstanceIdentifier.builder(iidBase) - .node(containerPlayerQname) - .node(leafQname) - .build(); - final PatchContext patch = new PatchContext(iidContext, List.of( - new PatchEntity("create data", CREATE, iidBase, buildBaseCont), - new PatchEntity("remove data", REMOVE, iidleaf), - new PatchEntity("delete data", DELETE, iidleaf)), "test patch id"); - - doNothing().when(readWrite).delete(LogicalDatastoreType.CONFIGURATION, iidleaf); + 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))); + + doNothing().when(readWrite).delete(LogicalDatastoreType.CONFIGURATION, GAP_IID); doReturn(immediateFalseFluentFuture()) - .when(readWrite).exists(LogicalDatastoreType.CONFIGURATION, iidBase); + .when(readWrite).exists(LogicalDatastoreType.CONFIGURATION, JUKEBOX_IID); doReturn(immediateFalseFluentFuture()) - .when(readWrite).exists(LogicalDatastoreType.CONFIGURATION, iidleaf); + .when(readWrite).exists(LogicalDatastoreType.CONFIGURATION, GAP_IID); doReturn(true).when(readWrite).cancel(); - final PatchStatusContext status = dataService.patchData(patch, uriInfo); - - assertFalse(status.isOk()); - assertEquals(3, status.getEditCollection().size()); - assertTrue(status.getEditCollection().get(0).isOk()); - assertTrue(status.getEditCollection().get(1).isOk()); - assertFalse(status.getEditCollection().get(2).isOk()); - assertFalse(status.getEditCollection().get(2).getEditErrors().isEmpty()); - final String errorMessage = status.getEditCollection().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); + 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()); + assertTrue(status.editCollection().get(0).isOk()); + assertTrue(status.editCollection().get(1).isOk()); + assertFalse(status.editCollection().get(2).isOk()); + assertFalse(status.editCollection().get(2).getEditErrors().isEmpty()); + final String errorMessage = status.editCollection().get(2).getEditErrors().get(0).getErrorMessage(); + assertEquals("Data does not exist", errorMessage); } }