Unit test for RestconfDataServiceImpl class 67/45167/4
authormiroslav.kovac <miroslav.kovac@pantheon.tech>
Mon, 5 Sep 2016 15:06:48 +0000 (17:06 +0200)
committerMiroslav Kovac <miroslav.kovac@pantheon.tech>
Tue, 6 Sep 2016 14:04:25 +0000 (14:04 +0000)
Change-Id: I9eac7cb5316465d3d7cc4b38f6d450bdb08015d7
Signed-off-by: miroslav.kovac <miroslav.kovac@pantheon.tech>
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/services/impl/RestconfDataServiceImpl.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/PatchDataTransactionUtil.java
restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/restful/services/impl/RestconfDataServiceImplTest.java [new file with mode: 0644]
restconf/sal-rest-connector/src/test/resources/jukebox/jukebox@2015-04-04.yang [deleted file]

index 23fe6fd7306a7dca5430a65a07c1d6bff0f0d9ce..c27da7ab621e240beac41202bccc7c5bab5bd86e 100644 (file)
@@ -10,6 +10,7 @@ package org.opendaylight.restconf.restful.services.impl;
 import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
 import java.text.SimpleDateFormat;
+import java.util.Date;
 import java.util.TimeZone;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriInfo;
@@ -52,7 +53,7 @@ public class RestconfDataServiceImpl implements RestconfDataService {
     private final TransactionChainHandler transactionChainHandler;
 
     public RestconfDataServiceImpl(final SchemaContextHandler schemaContextHandler,
-            final TransactionChainHandler transactionChainHandler) {
+                                   final TransactionChainHandler transactionChainHandler) {
         this.schemaContextHandler = schemaContextHandler;
         this.transactionChainHandler = transactionChainHandler;
     }
@@ -67,12 +68,13 @@ public class RestconfDataServiceImpl implements RestconfDataService {
         final DOMMountPoint mountPoint = instanceIdentifier.getMountPoint();
         final String value = uriInfo.getQueryParameters().getFirst(RestconfDataServiceConstant.CONTENT);
 
-        DOMTransactionChain transaction = null;
+        final DOMTransactionChain transaction;
         if (mountPoint == null) {
             transaction = this.transactionChainHandler.get();
         } else {
             transaction = transactionOfMountPoint(mountPoint);
         }
+
         final TransactionVarsWrapper transactionNode = new TransactionVarsWrapper(instanceIdentifier, mountPoint,
                 transaction);
         final NormalizedNode<?, ?> node = ReadDataTransactionUtil.readData(value, transactionNode);
@@ -86,11 +88,11 @@ public class RestconfDataServiceImpl implements RestconfDataService {
         dateFormatGmt.setTimeZone(TimeZone.getTimeZone("GMT"));
         final String etag = '"' + node.getNodeType().getModule().getFormattedRevision()
                 + node.getNodeType().getLocalName() + '"';
-        Response resp = null;
+        final Response resp;
 
         if ((value == null) || value.contains(RestconfDataServiceConstant.ReadData.CONFIG)) {
             resp = Response.status(200).entity(new NormalizedNodeContext(instanceIdentifier, node)).header("ETag", etag)
-                    .header("Last-Modified", dateFormatGmt.toString()).build();
+                    .header("Last-Modified", dateFormatGmt.format(new Date())).build();
         } else {
             resp = Response.status(200).entity(new NormalizedNodeContext(instanceIdentifier, node)).build();
         }
@@ -99,7 +101,6 @@ public class RestconfDataServiceImpl implements RestconfDataService {
 
     @Override
     public Response putData(final String identifier, final NormalizedNodeContext payload) {
-        Preconditions.checkNotNull(identifier);
         Preconditions.checkNotNull(payload);
 
         final InstanceIdentifierContext<? extends SchemaNode> iid = payload
@@ -110,8 +111,8 @@ public class RestconfDataServiceImpl implements RestconfDataService {
         PutDataTransactionUtil.validateListKeysEqualityInPayloadAndUri(payload);
 
         final DOMMountPoint mountPoint = payload.getInstanceIdentifierContext().getMountPoint();
-        DOMTransactionChain transaction = null;
-        SchemaContextRef ref = null;
+        final DOMTransactionChain transaction;
+        final SchemaContextRef ref;
         if (mountPoint == null) {
             transaction = this.transactionChainHandler.get();
             ref = new SchemaContextRef(this.schemaContextHandler.get());
@@ -135,8 +136,8 @@ public class RestconfDataServiceImpl implements RestconfDataService {
         Preconditions.checkNotNull(payload);
 
         final DOMMountPoint mountPoint = payload.getInstanceIdentifierContext().getMountPoint();
-        DOMTransactionChain transaction = null;
-        SchemaContextRef ref = null;
+        final DOMTransactionChain transaction;
+        final SchemaContextRef ref;
         if (mountPoint == null) {
             transaction = this.transactionChainHandler.get();
             ref = new SchemaContextRef(this.schemaContextHandler.get());
@@ -170,7 +171,6 @@ public class RestconfDataServiceImpl implements RestconfDataService {
 
     @Override
     public PATCHStatusContext patchData(final String identifier, final PATCHContext context, final UriInfo uriInfo) {
-        Preconditions.checkNotNull(identifier);
         return patchData(context, uriInfo);
     }
 
index 4741592977562a631056fe57949ff4f220ddeaa3..7890ee035b70b7adc4e859af0a8100a6a39413e3 100644 (file)
@@ -57,15 +57,14 @@ public final class PatchDataTransactionUtil {
     public static PATCHStatusContext patchData(final PATCHContext context, final TransactionVarsWrapper transactionNode,
                                                final SchemaContextRef schemaContextRef) {
         final List<PATCHStatusEntity> editCollection = new ArrayList<>();
-        int errorCounter = 0;
+        boolean noError = true;
         final DOMDataReadWriteTransaction tx = transactionNode.getTransactionChain().newReadWriteTransaction();
 
         for (final PATCHEntity patchEntity : context.getData()) {
             final PATCHEditOperation operation = PATCHEditOperation.valueOf(patchEntity.getOperation().toUpperCase());
-
-            switch (operation) {
-                case CREATE:
-                    if (errorCounter == 0) {
+            if (noError) {
+                switch (operation) {
+                    case CREATE:
                         try {
                             createDataWithinTransaction(LogicalDatastoreType.CONFIGURATION,
                                     patchEntity.getTargetNode(), patchEntity.getNode(), tx, schemaContextRef);
@@ -73,12 +72,10 @@ public final class PatchDataTransactionUtil {
                         } catch (final RestconfDocumentedException e) {
                             editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(),
                                     false, Lists.newArrayList(e.getErrors())));
-                            errorCounter++;
+                            noError = false;
                         }
-                    }
-                    break;
-                case DELETE:
-                    if (errorCounter == 0) {
+                        break;
+                    case DELETE:
                         try {
                             deleteDataWithinTransaction(LogicalDatastoreType.CONFIGURATION, patchEntity.getTargetNode(),
                                     tx);
@@ -86,12 +83,10 @@ public final class PatchDataTransactionUtil {
                         } catch (final RestconfDocumentedException e) {
                             editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(),
                                     false, Lists.newArrayList(e.getErrors())));
-                            errorCounter++;
+                            noError = false;
                         }
-                    }
-                    break;
-                case MERGE:
-                    if (errorCounter == 0) {
+                        break;
+                    case MERGE:
                         try {
                             mergeDataWithinTransaction(LogicalDatastoreType.CONFIGURATION,
                                     patchEntity.getTargetNode(), patchEntity.getNode(), tx, schemaContextRef);
@@ -99,12 +94,10 @@ public final class PatchDataTransactionUtil {
                         } catch (final RestconfDocumentedException e) {
                             editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(),
                                     false, Lists.newArrayList(e.getErrors())));
-                            errorCounter++;
+                            noError = false;
                         }
-                    }
-                    break;
-                case REPLACE:
-                    if (errorCounter == 0) {
+                        break;
+                    case REPLACE:
                         try {
                             replaceDataWithinTransaction(LogicalDatastoreType.CONFIGURATION,
                                     patchEntity.getTargetNode(), patchEntity.getNode(), schemaContextRef, tx);
@@ -112,12 +105,10 @@ public final class PatchDataTransactionUtil {
                         } catch (final RestconfDocumentedException e) {
                             editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(),
                                     false, Lists.newArrayList(e.getErrors())));
-                            errorCounter++;
+                            noError = false;
                         }
-                    }
-                    break;
-                case REMOVE:
-                    if (errorCounter == 0) {
+                        break;
+                    case REMOVE:
                         try {
                             removeDataWithinTransaction(LogicalDatastoreType.CONFIGURATION, patchEntity.getTargetNode(),
                                     tx);
@@ -125,21 +116,23 @@ public final class PatchDataTransactionUtil {
                         } catch (final RestconfDocumentedException e) {
                             editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(),
                                     false, Lists.newArrayList(e.getErrors())));
-                            errorCounter++;
+                            noError = false;
                         }
-                    }
-                    break;
-                default:
-                    editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(),
-                            false, Lists.newArrayList(new RestconfError(ErrorType.PROTOCOL,
-                            ErrorTag.OPERATION_NOT_SUPPORTED, "Not supported Yang PATCH operation"))));
-                    errorCounter++;
-                    break;
+                        break;
+                    default:
+                        editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(),
+                                false, Lists.newArrayList(new RestconfError(ErrorType.PROTOCOL,
+                                ErrorTag.OPERATION_NOT_SUPPORTED, "Not supported Yang PATCH operation"))));
+                        noError = false;
+                        break;
+                }
+            } else {
+                break;
             }
         }
 
         // if no errors then submit transaction, otherwise cancel
-        if (errorCounter == 0) {
+        if (noError) {
             final ResponseFactory response = new ResponseFactory();
             final CheckedFuture<Void, TransactionCommitFailedException> future = tx.submit();
 
@@ -155,7 +148,8 @@ public final class PatchDataTransactionUtil {
         } else {
             tx.cancel();
             RestConnectorProvider.resetTransactionChainForAdapaters(transactionNode.getTransactionChain());
-            return new PATCHStatusContext(context.getPatchId(), ImmutableList.copyOf(editCollection), false, null);
+            return new PATCHStatusContext(context.getPatchId(), ImmutableList.copyOf(editCollection),
+                    false, null);
         }
     }
 
@@ -288,7 +282,7 @@ public final class PatchDataTransactionUtil {
      * @param path Path to be checked
      */
     public static void checkItemExistsWithinTransaction(final DOMDataReadWriteTransaction rWTransaction,
-                                       final LogicalDatastoreType store, final YangInstanceIdentifier path) {
+                                                        final LogicalDatastoreType store, final YangInstanceIdentifier path) {
         final CheckedFuture<Boolean, ReadFailedException> future = rWTransaction.exists(store, path);
         final FutureDataFactory<Boolean> response = new FutureDataFactory<>();
 
@@ -310,7 +304,7 @@ public final class PatchDataTransactionUtil {
      * @param path Path to be checked
      */
     public static void checkItemDoesNotExistsWithinTransaction(final DOMDataReadWriteTransaction rWTransaction,
-                                              final LogicalDatastoreType store, final YangInstanceIdentifier path) {
+                                                               final LogicalDatastoreType store, final YangInstanceIdentifier path) {
         final CheckedFuture<Boolean, ReadFailedException> future = rWTransaction.exists(store, path);
         final FutureDataFactory<Boolean> response = new FutureDataFactory<>();
 
@@ -323,4 +317,4 @@ public final class PatchDataTransactionUtil {
                     "Data already exists", ErrorType.PROTOCOL, ErrorTag.DATA_EXISTS, path);
         }
     }
-}
+}
\ No newline at end of file
diff --git a/restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/restful/services/impl/RestconfDataServiceImplTest.java b/restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/restful/services/impl/RestconfDataServiceImplTest.java
new file mode 100644 (file)
index 0000000..7efab97
--- /dev/null
@@ -0,0 +1,315 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.restconf.restful.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.assertTrue;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.Futures;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.List;
+import javax.ws.rs.core.MultivaluedHashMap;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
+import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
+import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils;
+import org.opendaylight.netconf.sal.restconf.impl.InstanceIdentifierContext;
+import org.opendaylight.netconf.sal.restconf.impl.NormalizedNodeContext;
+import org.opendaylight.netconf.sal.restconf.impl.PATCHContext;
+import org.opendaylight.netconf.sal.restconf.impl.PATCHEntity;
+import org.opendaylight.netconf.sal.restconf.impl.PATCHStatusContext;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
+import org.opendaylight.restconf.RestConnectorProvider;
+import org.opendaylight.restconf.common.references.SchemaContextRef;
+import org.opendaylight.restconf.handlers.SchemaContextHandler;
+import org.opendaylight.restconf.handlers.TransactionChainHandler;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+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.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+
+public class RestconfDataServiceImplTest {
+
+    private static final String PATH_FOR_NEW_SCHEMA_CONTEXT = "/jukebox";
+
+    private ContainerNode buildBaseCont;
+    private SchemaContextRef contextRef;
+    private YangInstanceIdentifier iidBase;
+    private DataSchemaNode schemaNode;
+    private RestconfDataServiceImpl dataService;
+    private QName baseQName;
+    private QName containerQname;
+    private QName leafQname;
+    private ContainerNode buildBaseContToReplace;
+
+    @Mock
+    private TransactionChainHandler transactionChainHandler;
+    @Mock
+    private DOMTransactionChain domTransactionChain;
+    @Mock
+    private UriInfo uriInfo;
+    @Mock
+    private DOMDataReadWriteTransaction readWrite;
+    @Mock
+    private DOMDataReadOnlyTransaction read;
+    @Mock
+    private DOMDataWriteTransaction write;
+
+    @Before
+    public void setUp() throws Exception {
+        baseQName = QName.create("http://example.com/ns/example-jukebox", "2015-04-04", "jukebox");
+        containerQname = QName.create(baseQName, "player");
+        leafQname = QName.create(baseQName, "gap");
+        LeafNode buildLeaf = Builders.leafBuilder()
+                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(leafQname))
+                .withValue(0.2)
+                .build();
+
+        ContainerNode buildPlayerCont = Builders.containerBuilder()
+                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(containerQname))
+                .withChild(buildLeaf)
+                .build();
+        buildBaseCont = Builders.containerBuilder()
+                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(baseQName))
+                .withChild(buildPlayerCont)
+                .build();
+        buildLeaf = Builders.leafBuilder()
+                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(leafQname))
+                .withValue(0.5)
+                .build();
+        buildPlayerCont = Builders.containerBuilder()
+                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(containerQname))
+                .withChild(buildLeaf)
+                .build();
+        buildBaseContToReplace = Builders.containerBuilder()
+                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(baseQName))
+                .withChild(buildPlayerCont)
+                .build();
+        iidBase = YangInstanceIdentifier.builder()
+                .node(baseQName)
+                .build();
+
+        contextRef = new SchemaContextRef(TestRestconfUtils.loadSchemaContext(PATH_FOR_NEW_SCHEMA_CONTEXT));
+        schemaNode = DataSchemaContextTree.from(contextRef.get()).getChild(iidBase).getDataSchemaNode();
+        MockitoAnnotations.initMocks(this);
+        final SchemaContextHandler schemaContextHandler = new SchemaContextHandler();
+
+        schemaContextHandler.onGlobalContextUpdated(contextRef.get());
+        dataService = new RestconfDataServiceImpl(schemaContextHandler, transactionChainHandler);
+        doReturn(domTransactionChain).when(transactionChainHandler).get();
+        doReturn(read).when(domTransactionChain).newReadOnlyTransaction();
+        doReturn(readWrite).when(domTransactionChain).newReadWriteTransaction();
+        doReturn(write).when(domTransactionChain).newWriteOnlyTransaction();
+    }
+
+    @Test
+    public void testReadData() {
+        doReturn(new MultivaluedHashMap<String, String>()).when(uriInfo).getQueryParameters();
+        doReturn(Futures.immediateCheckedFuture(Optional.of(buildBaseCont))).when(read)
+                .read(LogicalDatastoreType.CONFIGURATION, iidBase);
+        doReturn(Futures.immediateCheckedFuture(Optional.absent())).when(read).read(LogicalDatastoreType.OPERATIONAL, iidBase);
+        final Response response = dataService.readData("example-jukebox:jukebox", uriInfo);
+        assertNotNull(response);
+        assertEquals(200, response.getStatus());
+        assertEquals(buildBaseCont, ((NormalizedNodeContext) response.getEntity()).getData());
+    }
+
+    @Test(expected = RestconfDocumentedException.class)
+    public void testReadDataNoData() {
+        doReturn(new MultivaluedHashMap<String, String>()).when(uriInfo).getQueryParameters();
+        doReturn(Futures.immediateCheckedFuture(Optional.absent())).when(read).read(LogicalDatastoreType.CONFIGURATION,
+                iidBase);
+        doReturn(Futures.immediateCheckedFuture(Optional.absent())).when(read).read(LogicalDatastoreType.OPERATIONAL,
+                iidBase);
+        final Response response = dataService.readData("example-jukebox:jukebox", uriInfo);
+    }
+
+    @Test
+    public void testPutData() {
+        final InstanceIdentifierContext<DataSchemaNode> iidContext = new InstanceIdentifierContext<>(iidBase, schemaNode, null, contextRef.get());
+        final NormalizedNodeContext payload = new NormalizedNodeContext(iidContext, buildBaseCont);
+
+        doReturn(Futures.immediateCheckedFuture(Optional.of(buildBaseCont))).when(read)
+                .read(LogicalDatastoreType.CONFIGURATION, iidBase);
+        doNothing().when(write).put(LogicalDatastoreType.CONFIGURATION, iidBase, payload.getData());
+        doReturn(Futures.immediateCheckedFuture(null)).when(write).submit();
+        final Response response = dataService.putData(null, payload);
+        assertNotNull(response);
+        assertEquals(200, response.getStatus());
+    }
+
+    @Test
+    public void testPutDataWithMountPoint() {
+        final DOMDataBroker dataBroker = Mockito.mock(DOMDataBroker.class);
+        final DOMMountPoint mountPoint = Mockito.mock(DOMMountPoint.class);
+        doReturn(Optional.of(dataBroker)).when(mountPoint).getService(DOMDataBroker.class);
+        doReturn(transactionChainHandler.get()).when(dataBroker).createTransactionChain(RestConnectorProvider.transactionListener);
+        final InstanceIdentifierContext<DataSchemaNode> iidContext = new InstanceIdentifierContext<>(iidBase, schemaNode, mountPoint, contextRef.get());
+        final NormalizedNodeContext payload = new NormalizedNodeContext(iidContext, buildBaseCont);
+
+        doReturn(Futures.immediateCheckedFuture(Optional.of(buildBaseCont))).when(read)
+                .read(LogicalDatastoreType.CONFIGURATION, iidBase);
+        doNothing().when(write).put(LogicalDatastoreType.CONFIGURATION, iidBase, payload.getData());
+        doReturn(Futures.immediateCheckedFuture(null)).when(write).submit();
+        final Response response = dataService.putData(null, payload);
+        assertNotNull(response);
+        assertEquals(200, response.getStatus());
+    }
+
+    @Test
+    public void testPostData() {
+        final QName listQname = QName.create(baseQName, "playlist");
+        final QName listKeyQname = QName.create(baseQName, "name");
+        final YangInstanceIdentifier.NodeIdentifierWithPredicates nodeWithKey =
+                new YangInstanceIdentifier.NodeIdentifierWithPredicates(listQname, listKeyQname, "name of band");
+        final LeafNode<Object> content = Builders.leafBuilder()
+                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(QName.create(baseQName, "name")))
+                .withValue("name of band")
+                .build();
+        final LeafNode<Object> content2 = Builders.leafBuilder()
+                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(QName.create(baseQName, "description")))
+                .withValue("band description")
+                .build();
+        final MapEntryNode mapEntryNode = Builders.mapEntryBuilder()
+                .withNodeIdentifier(nodeWithKey)
+                .withChild(content)
+                .withChild(content2)
+                .build();
+        final MapNode buildList = Builders.mapBuilder()
+                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(listQname))
+                .withChild(mapEntryNode)
+                .build();
+
+        doReturn(new MultivaluedHashMap<String, String>()).when(uriInfo).getQueryParameters();
+        final InstanceIdentifierContext<? extends SchemaNode> iidContext = new InstanceIdentifierContext<>(iidBase, null, null, contextRef.get());
+        final NormalizedNodeContext payload = new NormalizedNodeContext(iidContext, buildList);
+        doReturn(Futures.immediateCheckedFuture(Optional.absent())).when(read).read(LogicalDatastoreType.CONFIGURATION, iidBase);
+        final MapNode data = (MapNode) payload.getData();
+        final YangInstanceIdentifier.NodeIdentifierWithPredicates identifier = data.getValue().iterator().next().getIdentifier();
+        final YangInstanceIdentifier node = payload.getInstanceIdentifierContext().getInstanceIdentifier().node(identifier);
+        doReturn(Futures.immediateCheckedFuture(false)).when(readWrite).exists(LogicalDatastoreType.CONFIGURATION, node);
+        doNothing().when(readWrite).put(LogicalDatastoreType.CONFIGURATION, node, payload.getData());
+        doReturn(Futures.immediateCheckedFuture(null)).when(readWrite).submit();
+        doReturn(UriBuilder.fromUri("http://localhost:8181/restconf/15/")).when(uriInfo).getBaseUriBuilder();
+
+        final Response response = dataService.postData(null, payload, uriInfo);
+        assertEquals(201, response.getStatus());
+    }
+
+    @Test
+    public void testDeleteData() {
+        doNothing().when(readWrite).delete(LogicalDatastoreType.CONFIGURATION, iidBase);
+        doReturn(Futures.immediateCheckedFuture(null)).when(readWrite).submit();
+        doReturn(Futures.immediateCheckedFuture(true)).when(readWrite).exists(LogicalDatastoreType.CONFIGURATION, iidBase);
+        final Response response = dataService.deleteData("example-jukebox:jukebox");
+        assertNotNull(response);
+        assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
+    }
+
+    @Test
+    public void testPatchData() throws Exception {
+        final InstanceIdentifierContext<? extends SchemaNode> iidContext = new InstanceIdentifierContext<>(iidBase, schemaNode, null, contextRef.get());
+        final List<PATCHEntity> entity = new ArrayList<>();
+        final YangInstanceIdentifier iidleaf = YangInstanceIdentifier.builder(iidBase)
+                .node(containerQname)
+                .node(leafQname)
+                .build();
+        entity.add(new PATCHEntity("create data", "CREATE", iidBase, buildBaseCont));
+        entity.add(new PATCHEntity("replace data", "REPLACE", iidBase, buildBaseContToReplace));
+        entity.add(new PATCHEntity("delete data", "DELETE", iidleaf));
+        final PATCHContext patch = new PATCHContext(iidContext, entity, "test patch id");
+
+        doReturn(Futures.immediateCheckedFuture(Optional.of(buildBaseCont))).when(read)
+                .read(LogicalDatastoreType.CONFIGURATION, iidBase);
+        doNothing().when(write).put(LogicalDatastoreType.CONFIGURATION, iidBase, buildBaseCont);
+        doReturn(Futures.immediateCheckedFuture(null)).when(write).submit();
+        doNothing().when(readWrite).delete(LogicalDatastoreType.CONFIGURATION, iidleaf);
+        doReturn(Futures.immediateCheckedFuture(null)).when(readWrite).submit();
+        doReturn(Futures.immediateCheckedFuture(false)).when(readWrite).exists(LogicalDatastoreType.CONFIGURATION, iidBase);
+        doReturn(Futures.immediateCheckedFuture(true)).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());
+    }
+
+    @Test
+    public void testPatchDataDeleteNotExist() throws Exception {
+        final Field handler = RestConnectorProvider.class.getDeclaredField("transactionChainHandler");
+        final Field broker = RestConnectorProvider.class.getDeclaredField("dataBroker");
+
+        handler.setAccessible(true);
+        handler.set(RestConnectorProvider.class, mock(TransactionChainHandler.class));
+
+        broker.setAccessible(true);
+        broker.set(RestConnectorProvider.class, mock(DOMDataBroker.class));
+        final InstanceIdentifierContext<? extends SchemaNode> iidContext = new InstanceIdentifierContext<>(iidBase, schemaNode, null, contextRef.get());
+        final List<PATCHEntity> entity = new ArrayList<>();
+        final YangInstanceIdentifier iidleaf = YangInstanceIdentifier.builder(iidBase)
+                .node(containerQname)
+                .node(leafQname)
+                .build();
+        entity.add(new PATCHEntity("create data", "CREATE", iidBase, buildBaseCont));
+        entity.add(new PATCHEntity("remove data", "REMOVE", iidleaf));
+        entity.add(new PATCHEntity("delete data", "DELETE", iidleaf));
+        final PATCHContext patch = new PATCHContext(iidContext, entity, "test patch id");
+
+        doReturn(Futures.immediateCheckedFuture(Optional.of(buildBaseCont))).when(read)
+                .read(LogicalDatastoreType.CONFIGURATION, iidBase);
+        doNothing().when(write).put(LogicalDatastoreType.CONFIGURATION, iidBase, buildBaseCont);
+        doReturn(Futures.immediateCheckedFuture(null)).when(write).submit();
+        doNothing().when(readWrite).delete(LogicalDatastoreType.CONFIGURATION, iidleaf);
+        doReturn(Futures.immediateCheckedFuture(null)).when(readWrite).submit();
+        doReturn(Futures.immediateCheckedFuture(false)).when(readWrite).exists(LogicalDatastoreType.CONFIGURATION, iidBase);
+        doReturn(Futures.immediateCheckedFuture(false)).when(readWrite).exists(LogicalDatastoreType.CONFIGURATION, iidleaf);
+        doReturn(true).when(readWrite).cancel();
+        final PATCHStatusContext status = dataService.patchData(patch, uriInfo);
+
+        handler.set(RestConnectorProvider.class, null);
+        handler.setAccessible(false);
+
+        broker.set(RestConnectorProvider.class, null);
+        broker.setAccessible(false);
+
+        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);
+    }
+
+}
\ No newline at end of file
diff --git a/restconf/sal-rest-connector/src/test/resources/jukebox/jukebox@2015-04-04.yang b/restconf/sal-rest-connector/src/test/resources/jukebox/jukebox@2015-04-04.yang
deleted file mode 100644 (file)
index 5c319e1..0000000
+++ /dev/null
@@ -1,243 +0,0 @@
-module example-jukebox {
-
-      namespace "http://example.com/ns/example-jukebox";
-      prefix "jbox";
-
-      organization "Example, Inc.";
-      contact "support at example.com";
-      description "Example Jukebox Data Model Module";
-      revision "2015-04-04" {
-        description "Initial version.";
-        reference "example.com document 1-4673";
-      }
-
-      identity genre {
-        description "Base for all genre types";
-      }
-
-      // abbreviated list of genre classifications
-      identity alternative {
-        base genre;
-        description "Alternative music";
-      }
-      identity blues {
-        base genre;
-        description "Blues music";
-      }
-      identity country {
-        base genre;
-        description "Country music";
-      }
-      identity jazz {
-        base genre;
-        description "Jazz music";
-      }
-      identity pop {
-        base genre;
-        description "Pop music";
-      }
-      identity rock {
-        base genre;
-        description "Rock music";
-      }
-
-      container jukebox {
-        presence
-          "An empty container indicates that the jukebox
-           service is available";
-
-        description
-          "Represents a jukebox resource, with a library, playlists,
-           and a play operation.";
-
-        container library {
-
-          description "Represents the jukebox library resource.";
-
-          list artist {
-            key name;
-
-            description
-              "Represents one artist resource within the
-               jukebox library resource.";
-
-            leaf name {
-              type string {
-                length "1 .. max";
-              }
-              description "The name of the artist.";
-            }
-
-            list album {
-              key name;
-
-              description
-                "Represents one album resource within one
-                 artist resource, within the jukebox library.";
-
-              leaf name {
-                type string {
-                  length "1 .. max";
-                }
-                description "The name of the album.";
-              }
-
-              leaf genre {
-                type identityref { base genre; }
-                description
-                  "The genre identifying the type of music on
-                   the album.";
-              }
-
-              leaf year {
-                type uint16 {
-                  range "1900 .. max";
-                }
-                description "The year the album was released";
-              }
-
-              container admin {
-                description
-                  "Administrative information for the album.";
-
-                leaf label {
-                  type string;
-                  description "The label that released the album.";
-                }
-                leaf catalogue-number {
-                  type string;
-                  description "The album's catalogue number.";
-                }
-              }
-
-              list song {
-                key name;
-
-                description
-                  "Represents one song resource within one
-                   album resource, within the jukebox library.";
-
-                leaf name {
-                  type string {
-                     length "1 .. max";
-                  }
-                  description "The name of the song";
-                }
-                leaf location {
-                  type string;
-                  mandatory true;
-                  description
-                   "The file location string of the
-                    media file for the song";
-                }
-                leaf format {
-                  type string;
-                  description
-                    "An identifier string for the media type
-                     for the file associated with the
-                     'location' leaf for this entry.";
-                }
-                leaf length {
-                  type uint32;
-                  units "seconds";
-                  description
-                    "The duration of this song in seconds.";
-                }
-              }   // end list 'song'
-            }   // end list 'album'
-          }  // end list 'artist'
-
-          leaf artist-count {
-             type uint32;
-             units "songs";
-             config false;
-             description "Number of artists in the library";
-          }
-          leaf album-count {
-             type uint32;
-             units "albums";
-             config false;
-             description "Number of albums in the library";
-          }
-          leaf song-count {
-             type uint32;
-             units "songs";
-             config false;
-             description "Number of songs in the library";
-          }
-        }  // end library
-
-        list playlist {
-          key name;
-
-          description
-            "Example configuration data resource";
-
-          leaf name {
-            type string;
-            description
-              "The name of the playlist.";
-          }
-          leaf description {
-            type string;
-            description
-              "A comment describing the playlist.";
-          }
-
-          list song {
-            key index;
-            ordered-by user;
-
-            description
-              "Example nested configuration data resource";
-
-            leaf index {    // not really needed
-              type uint32;
-              description
-                "An arbitrary integer index for this playlist song.";
-            }
-            leaf id {
-              type leafref {
-                path "/jbox:jukebox/jbox:library/jbox:artist/" +
-                     "jbox:album/jbox:song/jbox:name";
-              }
-              mandatory true;
-              description
-                "Song identifier. Must identify an instance of
-                 /jukebox/library/artist/album/song/name.";
-            }
-          }
-        }
-
-        container player {
-          description
-            "Represents the jukebox player resource.";
-
-          leaf gap {
-            type decimal64 {
-              fraction-digits 1;
-              range "0.0 .. 2.0";
-            }
-            units "tenths of seconds";
-            description "Time gap between each song";
-          }
-        }
-      }
-
-      rpc play {
-        description "Control function for the jukebox player";
-        input {
-          leaf playlist {
-            type string;
-            mandatory true;
-            description "playlist name";
-          }
-
-          leaf song-number {
-            type uint32;
-            mandatory true;
-            description "Song number in playlist to play";
-          }
-        }
-      }
-   }