Bug 6272 - support RESTCONF PATCH for mounted NETCONF nodes 12/51712/1
authorIvan Hrasko <ivan.hrasko@pantheon.tech>
Fri, 23 Sep 2016 11:33:13 +0000 (13:33 +0200)
committerJakub Toth <jatoth@cisco.com>
Sat, 11 Feb 2017 01:32:53 +0000 (02:32 +0100)
- solution for Restconf draft17
- make mount point service available when identifier is parsed
- added unit tests

Change-Id: I7133efe7d7517ff49ff9caee42f1723d491973d5
Signed-off-by: Jakub Toth <jatoth@cisco.com>
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/RestConnectorProvider.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/jersey/providers/AbstractIdentifierAwareJaxRsProvider.java
restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/jersey/providers/AbstractBodyReaderTest.java [moved from restconf/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/rest/impl/test/providers/Draft17AbstractBodyReaderTest.java with 78% similarity]
restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/jersey/providers/JsonBodyReaderTest.java [new file with mode: 0644]
restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/jersey/providers/JsonPATCHBodyReaderMountPointTest.java [new file with mode: 0644]
restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/jersey/providers/JsonPATCHBodyReaderTest.java [moved from restconf/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/rest/impl/test/providers/TestDraft17JsonPATCHBodyReader.java with 92% similarity]
restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/jersey/providers/XmlBodyReaderTest.java [new file with mode: 0644]
restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/jersey/providers/XmlPATCHBodyReaderMountPointTest.java [new file with mode: 0644]
restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/jersey/providers/XmlPATCHBodyReaderTest.java [moved from restconf/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/rest/impl/test/providers/TestDraft17XmlPATCHBodyReader.java with 92% similarity]
restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/restful/services/impl/RestconfDataServiceImplTest.java

index 50012e643b4df92fca954fa7c407fc43f384be58..846320049d4f2bbb46e47698f8cd6967e395d7f3 100644 (file)
@@ -59,6 +59,7 @@ public class RestConnectorProvider implements Provider, RestConnector, AutoClose
     private ListenerRegistration<SchemaContextListener> listenerRegistration;
     private static TransactionChainHandler transactionChainHandler;
     private static DOMDataBroker dataBroker;
+    private static DOMMountPointServiceHandler mountPointServiceHandler;
 
     @Override
     public void onSessionInitiated(final ProviderSession session) {
@@ -69,7 +70,7 @@ public class RestConnectorProvider implements Provider, RestConnector, AutoClose
         final SchemaContextHandler schemaCtxHandler = new SchemaContextHandler();
         this.listenerRegistration = schemaService.registerSchemaContextListener(schemaCtxHandler);
 
-        final DOMMountPointServiceHandler domMountPointServiceHandler = new DOMMountPointServiceHandler(
+        RestConnectorProvider.mountPointServiceHandler = new DOMMountPointServiceHandler(
                 session.getService(DOMMountPointService.class));
 
         RestConnectorProvider.dataBroker = session.getService(DOMDataBroker.class);
@@ -81,7 +82,7 @@ public class RestConnectorProvider implements Provider, RestConnector, AutoClose
         final DOMRpcService rpcService = session.getService(DOMRpcService.class);
         final RpcServiceHandler rpcServiceHandler = new RpcServiceHandler(rpcService);
 
-        wrapperServices.setHandlers(schemaCtxHandler, domMountPointServiceHandler,
+        wrapperServices.setHandlers(schemaCtxHandler, RestConnectorProvider.mountPointServiceHandler,
                 RestConnectorProvider.transactionChainHandler, brokerHandler, rpcServiceHandler);
     }
 
@@ -100,6 +101,14 @@ public class RestConnectorProvider implements Provider, RestConnector, AutoClose
         );
     }
 
+    /**
+     * Get current {@link DOMMountPointService} from {@link DOMMountPointServiceHandler}.
+     * @return {@link DOMMountPointService}
+     */
+    public static DOMMountPointService getMountPointService() {
+        return RestConnectorProvider.mountPointServiceHandler.get();
+    }
+
     @Override
     public Collection<ProviderFunctionality> getProviderFunctionality() {
         return Collections.emptySet();
index 518d76631444fd4569eb02e767469280f9bd8759..69ebec275128f95f4eaaec1cf7fb6f0cbc96014b 100644 (file)
@@ -15,6 +15,7 @@ import javax.ws.rs.core.UriInfo;
 import org.opendaylight.netconf.sal.rest.api.RestconfConstants;
 import org.opendaylight.netconf.sal.restconf.impl.ControllerContext;
 import org.opendaylight.netconf.sal.restconf.impl.InstanceIdentifierContext;
+import org.opendaylight.restconf.RestConnectorProvider;
 import org.opendaylight.restconf.utils.parser.ParserIdentifier;
 
 public class AbstractIdentifierAwareJaxRsProvider {
@@ -32,8 +33,10 @@ public class AbstractIdentifierAwareJaxRsProvider {
     }
 
     protected InstanceIdentifierContext<?> getInstanceIdentifierContext() {
-        return ParserIdentifier.toInstanceIdentifier(getIdentifier(),
-                ControllerContext.getInstance().getGlobalSchema(), Optional.absent());
+        return ParserIdentifier.toInstanceIdentifier(
+                getIdentifier(),
+                ControllerContext.getInstance().getGlobalSchema(),
+                Optional.of(RestConnectorProvider.getMountPointService()));
     }
 
     protected UriInfo getUriInfo() {
@@ -6,7 +6,7 @@
  * and is available at http://www.eclipse.org/legal/epl-v10.html
  */
 
-package org.opendaylight.controller.sal.rest.impl.test.providers;
+package org.opendaylight.restconf.jersey.providers;
 
 import static org.junit.Assert.assertNotNull;
 import static org.mockito.Mockito.mock;
@@ -24,25 +24,34 @@ import org.opendaylight.netconf.sal.rest.api.RestconfConstants;
 import org.opendaylight.netconf.sal.restconf.impl.ControllerContext;
 import org.opendaylight.netconf.sal.restconf.impl.NormalizedNodeContext;
 import org.opendaylight.netconf.sal.restconf.impl.PATCHContext;
-import org.opendaylight.restconf.jersey.providers.AbstractIdentifierAwareJaxRsProvider;
+import org.opendaylight.restconf.RestConnectorProvider;
+import org.opendaylight.restconf.handlers.DOMMountPointServiceHandler;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 
-public abstract class Draft17AbstractBodyReaderTest {
+abstract class AbstractBodyReaderTest {
 
     protected final static ControllerContext controllerContext = ControllerContext.getInstance();
     protected final MediaType mediaType;
     private static Field uriField;
     private static Field requestField;
+    protected final static DOMMountPointServiceHandler mountPointServiceHandler = mock(
+            DOMMountPointServiceHandler.class);
 
-    public Draft17AbstractBodyReaderTest() throws NoSuchFieldException,
-            SecurityException {
+    AbstractBodyReaderTest() throws NoSuchFieldException, IllegalAccessException {
         uriField = AbstractIdentifierAwareJaxRsProvider.class
                 .getDeclaredField("uriInfo");
         uriField.setAccessible(true);
+
         requestField = AbstractIdentifierAwareJaxRsProvider.class
                 .getDeclaredField("request");
         requestField.setAccessible(true);
+
         mediaType = getMediaType();
+
+        final Field mountPointServiceHandlerField = RestConnectorProvider.class.
+                getDeclaredField("mountPointServiceHandler");
+        mountPointServiceHandlerField.setAccessible(true);
+        mountPointServiceHandlerField.set(RestConnectorProvider.class, mountPointServiceHandler);
     }
 
     protected abstract MediaType getMediaType();
@@ -78,12 +87,6 @@ public abstract class Draft17AbstractBodyReaderTest {
         requestField.set(normalizedNodeProvider, request);
     }
 
-    protected static void checkMountPointNormalizedNodeContext(
-            final NormalizedNodeContext nnContext) {
-        checkNormalizedNodeContext(nnContext);
-        assertNotNull(nnContext.getInstanceIdentifierContext().getMountPoint());
-    }
-
     protected static void checkNormalizedNodeContext(
             final NormalizedNodeContext nnContext) {
         assertNotNull(nnContext.getData());
@@ -100,4 +103,10 @@ public abstract class Draft17AbstractBodyReaderTest {
         assertNotNull(patchContext.getInstanceIdentifierContext().getSchemaContext());
         assertNotNull(patchContext.getInstanceIdentifierContext().getSchemaNode());
     }
+
+    protected static void checkPATCHContextMountPoint(final PATCHContext patchContext) {
+        checkPATCHContext(patchContext);
+        assertNotNull(patchContext.getInstanceIdentifierContext().getMountPoint());
+        assertNotNull(patchContext.getInstanceIdentifierContext().getMountPoint().getSchemaContext());
+    }
 }
diff --git a/restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/jersey/providers/JsonBodyReaderTest.java b/restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/jersey/providers/JsonBodyReaderTest.java
new file mode 100644 (file)
index 0000000..80b147b
--- /dev/null
@@ -0,0 +1,177 @@
+/*
+ * 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.jersey.providers;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import com.google.common.collect.Sets;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.net.URI;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Collection;
+import javax.ws.rs.core.MediaType;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
+import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils;
+import org.opendaylight.controller.sal.rest.impl.test.providers.TestXmlBodyReader;
+import org.opendaylight.netconf.sal.restconf.impl.NormalizedNodeContext;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+
+public class JsonBodyReaderTest extends AbstractBodyReaderTest {
+
+    private final JsonNormalizedNodeBodyReader jsonBodyReader;
+    private static SchemaContext schemaContext;
+
+    private static final QNameModule INSTANCE_IDENTIFIER_MODULE_QNAME = initializeInstanceIdentifierModule();
+
+    private static QNameModule initializeInstanceIdentifierModule() {
+        try {
+            return QNameModule.create(URI.create("instance:identifier:module"),
+                    new SimpleDateFormat("yyyy-MM-dd").parse("2014-01-17"));
+        } catch (final ParseException e) {
+            throw new Error(e);
+        }
+    }
+
+    public JsonBodyReaderTest() throws Exception {
+        super();
+        this.jsonBodyReader = new JsonNormalizedNodeBodyReader();
+    }
+
+    @Override
+    protected MediaType getMediaType() {
+        return new MediaType(MediaType.APPLICATION_XML, null);
+    }
+
+    @BeforeClass
+    public static void initialization()
+            throws NoSuchFieldException, SecurityException, FileNotFoundException, SourceException, ReactorException {
+        final Collection<File> testFiles = TestRestconfUtils.loadFiles("/instanceidentifier/yang");
+        testFiles.addAll(TestRestconfUtils.loadFiles("/modules"));
+        testFiles.addAll(TestRestconfUtils.loadFiles("/invoke-rpc"));
+        schemaContext = TestRestconfUtils.parseYangSources(testFiles);
+        controllerContext.setSchemas(schemaContext);
+        when(mountPointServiceHandler.get()).thenReturn(mock(DOMMountPointService.class));
+    }
+
+    @Test
+    public void moduleDataTest() throws Exception {
+        final DataSchemaNode dataSchemaNode = schemaContext
+                .getDataChildByName(QName.create(INSTANCE_IDENTIFIER_MODULE_QNAME, "cont"));
+        final YangInstanceIdentifier dataII = YangInstanceIdentifier.of(dataSchemaNode.getQName());
+        final String uri = "instance-identifier-module:cont";
+        mockBodyReader(uri, this.jsonBodyReader, false);
+        final InputStream inputStream = JsonBodyReaderTest.class
+                .getResourceAsStream("/instanceidentifier/json/jsondata.json");
+        final NormalizedNodeContext returnValue = this.jsonBodyReader.readFrom(null, null, null, this.mediaType, null,
+                inputStream);
+        checkNormalizedNodeContext(returnValue);
+        checkExpectValueNormalizeNodeContext(dataSchemaNode, returnValue, dataII);
+    }
+
+    @Test
+    public void moduleSubContainerDataPutTest() throws Exception {
+        final DataSchemaNode dataSchemaNode = schemaContext
+                .getDataChildByName(QName.create(INSTANCE_IDENTIFIER_MODULE_QNAME, "cont"));
+        final QName cont1QName = QName.create(dataSchemaNode.getQName(), "cont1");
+        final YangInstanceIdentifier dataII = YangInstanceIdentifier.of(dataSchemaNode.getQName()).node(cont1QName);
+        final DataSchemaNode dataSchemaNodeOnPath = ((DataNodeContainer) dataSchemaNode).getDataChildByName(cont1QName);
+        final String uri = "instance-identifier-module:cont/cont1";
+        mockBodyReader(uri, this.jsonBodyReader, false);
+        final InputStream inputStream = JsonBodyReaderTest.class
+                .getResourceAsStream("/instanceidentifier/json/json_sub_container.json");
+        final NormalizedNodeContext returnValue = this.jsonBodyReader.readFrom(null, null, null, this.mediaType, null,
+                inputStream);
+        checkNormalizedNodeContext(returnValue);
+        checkExpectValueNormalizeNodeContext(dataSchemaNodeOnPath, returnValue, dataII);
+    }
+
+    @Test
+    public void moduleSubContainerDataPostTest() throws Exception {
+        final DataSchemaNode dataSchemaNode = schemaContext
+                .getDataChildByName(QName.create(INSTANCE_IDENTIFIER_MODULE_QNAME, "cont"));
+        final QName cont1QName = QName.create(dataSchemaNode.getQName(), "cont1");
+        final YangInstanceIdentifier dataII = YangInstanceIdentifier.of(dataSchemaNode.getQName()).node(cont1QName);
+        final String uri = "instance-identifier-module:cont";
+        mockBodyReader(uri, this.jsonBodyReader, true);
+        final InputStream inputStream = JsonBodyReaderTest.class
+                .getResourceAsStream("/instanceidentifier/json/json_sub_container.json");
+        final NormalizedNodeContext returnValue = this.jsonBodyReader.readFrom(null, null, null, this.mediaType, null,
+                inputStream);
+        checkNormalizedNodeContext(returnValue);
+        checkExpectValueNormalizeNodeContext(dataSchemaNode, returnValue, dataII);
+    }
+
+    @Test
+    public void moduleSubContainerAugmentDataPostTest() throws Exception {
+        final DataSchemaNode dataSchemaNode = schemaContext
+                .getDataChildByName(QName.create(INSTANCE_IDENTIFIER_MODULE_QNAME, "cont"));
+        final Module augmentModule = schemaContext.findModuleByNamespace(new URI("augment:module")).iterator().next();
+        final QName contAugmentQName = QName.create(augmentModule.getQNameModule(), "cont-augment");
+        final YangInstanceIdentifier.AugmentationIdentifier augII = new YangInstanceIdentifier.AugmentationIdentifier(
+                Sets.newHashSet(contAugmentQName));
+        final YangInstanceIdentifier dataII = YangInstanceIdentifier.of(dataSchemaNode.getQName()).node(augII)
+                .node(contAugmentQName);
+        final String uri = "instance-identifier-module:cont";
+        mockBodyReader(uri, this.jsonBodyReader, true);
+        final InputStream inputStream = TestXmlBodyReader.class
+                .getResourceAsStream("/instanceidentifier/json/json_augment_container.json");
+        final NormalizedNodeContext returnValue = this.jsonBodyReader.readFrom(null, null, null, this.mediaType, null,
+                inputStream);
+        checkNormalizedNodeContext(returnValue);
+        checkExpectValueNormalizeNodeContext(dataSchemaNode, returnValue, dataII);
+    }
+
+    @Test
+    public void moduleSubContainerChoiceAugmentDataPostTest() throws Exception {
+        final DataSchemaNode dataSchemaNode = schemaContext
+                .getDataChildByName(QName.create(INSTANCE_IDENTIFIER_MODULE_QNAME, "cont"));
+        final Module augmentModule = schemaContext.findModuleByNamespace(new URI("augment:module")).iterator().next();
+        final QName augmentChoice1QName = QName.create(augmentModule.getQNameModule(), "augment-choice1");
+        final QName augmentChoice2QName = QName.create(augmentChoice1QName, "augment-choice2");
+        final QName containerQName = QName.create(augmentChoice1QName, "case-choice-case-container1");
+        final YangInstanceIdentifier.AugmentationIdentifier augChoice1II = new YangInstanceIdentifier.AugmentationIdentifier(
+                Sets.newHashSet(augmentChoice1QName));
+        final YangInstanceIdentifier.AugmentationIdentifier augChoice2II = new YangInstanceIdentifier.AugmentationIdentifier(
+                Sets.newHashSet(augmentChoice2QName));
+        final YangInstanceIdentifier dataII = YangInstanceIdentifier.of(dataSchemaNode.getQName()).node(augChoice1II)
+                .node(augmentChoice1QName).node(augChoice2II).node(augmentChoice2QName).node(containerQName);
+        final String uri = "instance-identifier-module:cont";
+        mockBodyReader(uri, this.jsonBodyReader, true);
+        final InputStream inputStream = TestXmlBodyReader.class
+                .getResourceAsStream("/instanceidentifier/json/json_augment_choice_container.json");
+        final NormalizedNodeContext returnValue = this.jsonBodyReader.readFrom(null, null, null, this.mediaType, null,
+                inputStream);
+        checkNormalizedNodeContext(returnValue);
+        checkExpectValueNormalizeNodeContext(dataSchemaNode, returnValue, dataII);
+    }
+
+    private void checkExpectValueNormalizeNodeContext(final DataSchemaNode dataSchemaNode,
+            final NormalizedNodeContext nnContext, final YangInstanceIdentifier dataNodeIdent) {
+        assertEquals(dataSchemaNode, nnContext.getInstanceIdentifierContext().getSchemaNode());
+        assertEquals(dataNodeIdent, nnContext.getInstanceIdentifierContext().getInstanceIdentifier());
+        assertNotNull(NormalizedNodes.findNode(nnContext.getData(), dataNodeIdent));
+    }
+}
\ No newline at end of file
diff --git a/restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/jersey/providers/JsonPATCHBodyReaderMountPointTest.java b/restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/jersey/providers/JsonPATCHBodyReaderMountPointTest.java
new file mode 100644 (file)
index 0000000..4bce4fd
--- /dev/null
@@ -0,0 +1,177 @@
+/*
+ * 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.jersey.providers;
+
+import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import com.google.common.base.Optional;
+import java.io.InputStream;
+import javax.ws.rs.core.MediaType;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
+import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
+import org.opendaylight.controller.sal.rest.impl.test.providers.TestJsonBodyReader;
+import org.opendaylight.netconf.sal.restconf.impl.PATCHContext;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public class JsonPATCHBodyReaderMountPointTest extends AbstractBodyReaderTest {
+
+    private final JsonToPATCHBodyReader jsonPATCHBodyReader;
+    private static SchemaContext schemaContext;
+    private static final String MOUNT_POINT = "instance-identifier-module:cont/yang-ext:mount/";
+
+    public JsonPATCHBodyReaderMountPointTest() throws Exception {
+        super();
+        jsonPATCHBodyReader = new JsonToPATCHBodyReader();
+    }
+
+    @Override
+    protected MediaType getMediaType() {
+        return new MediaType(APPLICATION_JSON, null);
+    }
+
+    @BeforeClass
+    public static void initialization() {
+        schemaContext = schemaContextLoader("/instanceidentifier/yang", schemaContext);
+
+        final DOMMountPointService mountPointService = mock(DOMMountPointService.class);
+        final DOMMountPoint mountPoint = mock(DOMMountPoint.class);
+
+        when(mountPointServiceHandler.get()).thenReturn(mountPointService);
+        when(mountPointService.getMountPoint(any(YangInstanceIdentifier.class))).thenReturn(Optional.of(mountPoint));
+        when(mountPoint.getSchemaContext()).thenReturn(schemaContext);
+
+        controllerContext.setSchemas(schemaContext);
+    }
+
+    @Test
+    public void modulePATCHDataTest() throws Exception {
+        final String uri = MOUNT_POINT + "instance-identifier-patch-module:patch-cont/my-list1=leaf1";
+        mockBodyReader(uri, jsonPATCHBodyReader, false);
+
+        final InputStream inputStream = TestJsonBodyReader.class
+                .getResourceAsStream("/instanceidentifier/json/jsonPATCHdata.json");
+
+        final PATCHContext returnValue = jsonPATCHBodyReader
+                .readFrom(null, null, null, mediaType, null, inputStream);
+        checkPATCHContextMountPoint(returnValue);
+    }
+
+    /**
+     * Test of successful PATCH consisting of create and delete PATCH operations.
+     */
+    @Test
+    public void modulePATCHCreateAndDeleteTest() throws Exception {
+        final String uri = MOUNT_POINT + "instance-identifier-patch-module:patch-cont/my-list1=leaf1";
+        mockBodyReader(uri, jsonPATCHBodyReader, false);
+
+        final InputStream inputStream = TestJsonBodyReader.class
+                .getResourceAsStream("/instanceidentifier/json/jsonPATCHdataCreateAndDelete.json");
+
+        final PATCHContext returnValue = jsonPATCHBodyReader
+                .readFrom(null, null, null, mediaType, null, inputStream);
+        checkPATCHContextMountPoint(returnValue);
+    }
+
+    /**
+     * Test trying to use PATCH create operation which requires value without value. Test should fail with
+     * {@link RestconfDocumentedException} with error code 400.
+     */
+    @Test
+    public void modulePATCHValueMissingNegativeTest() throws Exception {
+        final String uri = MOUNT_POINT + "instance-identifier-patch-module:patch-cont/my-list1=leaf1";
+        mockBodyReader(uri, jsonPATCHBodyReader, false);
+
+        final InputStream inputStream = TestJsonBodyReader.class
+                .getResourceAsStream("/instanceidentifier/json/jsonPATCHdataValueMissing.json");
+
+        try {
+            jsonPATCHBodyReader.readFrom(null, null, null, mediaType, null, inputStream);
+            fail("Test should return error 400 due to missing value node when attempt to invoke create operation");
+        } catch (final RestconfDocumentedException e) {
+            assertEquals("Error code 400 expected", 400, e.getErrors().get(0).getErrorTag().getStatusCode());
+        }
+    }
+
+    /**
+     * Test trying to use value with PATCH delete operation which does not support value. Test should fail with
+     * {@link RestconfDocumentedException} with error code 400.
+     */
+    @Test
+    public void modulePATCHValueNotSupportedNegativeTest() throws Exception {
+        final String uri = MOUNT_POINT + "instance-identifier-patch-module:patch-cont/my-list1=leaf1";
+        mockBodyReader(uri, jsonPATCHBodyReader, false);
+
+        final InputStream inputStream = TestJsonBodyReader.class
+                .getResourceAsStream("/instanceidentifier/json/jsonPATCHdataValueNotSupported.json");
+
+        try {
+            jsonPATCHBodyReader.readFrom(null, null, null, mediaType, null, inputStream);
+            fail("Test should return error 400 due to present value node when attempt to invoke delete operation");
+        } catch (final RestconfDocumentedException e) {
+            assertEquals("Error code 400 expected", 400, e.getErrors().get(0).getErrorTag().getStatusCode());
+        }
+    }
+
+    /**
+     * Test using PATCH when target is completely specified in request URI and thus target leaf contains only '/' sign.
+     */
+    @Test
+    public void modulePATCHCompleteTargetInURITest() throws Exception {
+        final String uri = MOUNT_POINT + "instance-identifier-patch-module:patch-cont";
+        mockBodyReader(uri, jsonPATCHBodyReader, false);
+
+        final InputStream inputStream = TestJsonBodyReader.class
+                .getResourceAsStream("/instanceidentifier/json/jsonPATCHdataCompleteTargetInURI.json");
+
+        final PATCHContext returnValue = jsonPATCHBodyReader
+                .readFrom(null, null, null, mediaType, null, inputStream);
+        checkPATCHContextMountPoint(returnValue);
+    }
+
+    /**
+     * Test of Yang PATCH merge operation on list. Test consists of two edit operations - replace and merge.
+     */
+    @Test
+    public void modulePATCHMergeOperationOnListTest() throws Exception {
+        final String uri = MOUNT_POINT + "instance-identifier-patch-module:patch-cont/my-list1=leaf1";
+        mockBodyReader(uri, jsonPATCHBodyReader, false);
+
+        final InputStream inputStream = TestJsonBodyReader.class
+                .getResourceAsStream("/instanceidentifier/json/jsonPATCHMergeOperationOnList.json");
+
+        final PATCHContext returnValue = jsonPATCHBodyReader
+                .readFrom(null, null, null, mediaType, null, inputStream);
+        checkPATCHContextMountPoint(returnValue);
+    }
+
+    /**
+     * Test of Yang PATCH merge operation on container. Test consists of two edit operations - create and merge.
+     */
+    @Test
+    public void modulePATCHMergeOperationOnContainerTest() throws Exception {
+        final String uri = MOUNT_POINT + "instance-identifier-patch-module:patch-cont";
+        mockBodyReader(uri, jsonPATCHBodyReader, false);
+
+        final InputStream inputStream = TestJsonBodyReader.class
+                .getResourceAsStream("/instanceidentifier/json/jsonPATCHMergeOperationOnContainer.json");
+
+        final PATCHContext returnValue = jsonPATCHBodyReader
+                .readFrom(null, null, null, mediaType, null, inputStream);
+        checkPATCHContextMountPoint(returnValue);
+    }
+}
@@ -6,27 +6,30 @@
  * and is available at http://www.eclipse.org/legal/epl-v10.html
  */
 
-package org.opendaylight.controller.sal.rest.impl.test.providers;
+package org.opendaylight.restconf.jersey.providers;
 
 import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
 
 import java.io.InputStream;
 import javax.ws.rs.core.MediaType;
 import org.junit.BeforeClass;
 import org.junit.Test;
+import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
+import org.opendaylight.controller.sal.rest.impl.test.providers.TestJsonBodyReader;
 import org.opendaylight.netconf.sal.restconf.impl.PATCHContext;
 import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
-import org.opendaylight.restconf.jersey.providers.JsonToPATCHBodyReader;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 
-public class TestDraft17JsonPATCHBodyReader extends Draft17AbstractBodyReaderTest {
+public class JsonPATCHBodyReaderTest extends AbstractBodyReaderTest {
 
     private final JsonToPATCHBodyReader jsonPATCHBodyReader;
     private static SchemaContext schemaContext;
 
-    public TestDraft17JsonPATCHBodyReader() throws NoSuchFieldException, SecurityException {
+    public JsonPATCHBodyReaderTest() throws Exception {
         super();
         jsonPATCHBodyReader = new JsonToPATCHBodyReader();
     }
@@ -39,6 +42,7 @@ public class TestDraft17JsonPATCHBodyReader extends Draft17AbstractBodyReaderTes
     @BeforeClass
     public static void initialization() {
         schemaContext = schemaContextLoader("/instanceidentifier/yang", schemaContext);
+        when(mountPointServiceHandler.get()).thenReturn(mock(DOMMountPointService.class));
         controllerContext.setSchemas(schemaContext);
     }
 
diff --git a/restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/jersey/providers/XmlBodyReaderTest.java b/restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/jersey/providers/XmlBodyReaderTest.java
new file mode 100644 (file)
index 0000000..e1cbb96
--- /dev/null
@@ -0,0 +1,217 @@
+/*
+ * 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.jersey.providers;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import com.google.common.collect.Sets;
+import java.io.File;
+import java.io.InputStream;
+import java.net.URI;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Collection;
+import javax.ws.rs.core.MediaType;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
+import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils;
+import org.opendaylight.netconf.sal.restconf.impl.NormalizedNodeContext;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public class XmlBodyReaderTest extends AbstractBodyReaderTest {
+
+    private final XmlNormalizedNodeBodyReader xmlBodyReader;
+    private static SchemaContext schemaContext;
+    private static final QNameModule INSTANCE_IDENTIFIER_MODULE_QNAME = initializeInstanceIdentifierModule();
+
+    private static QNameModule initializeInstanceIdentifierModule() {
+        try {
+            return QNameModule.create(URI.create("instance:identifier:module"),
+                    new SimpleDateFormat("yyyy-MM-dd").parse("2014-01-17"));
+        } catch (final ParseException e) {
+            throw new Error(e);
+        }
+    }
+
+    public XmlBodyReaderTest() throws Exception {
+        super();
+        this.xmlBodyReader = new XmlNormalizedNodeBodyReader();
+    }
+
+    @Override
+    protected MediaType getMediaType() {
+        return new MediaType(MediaType.APPLICATION_XML, null);
+    }
+
+    @BeforeClass
+    public static void initialization() throws Exception {
+        final Collection<File> testFiles = TestRestconfUtils.loadFiles("/instanceidentifier/yang");
+        testFiles.addAll(TestRestconfUtils.loadFiles("/modules"));
+        testFiles.addAll(TestRestconfUtils.loadFiles("/invoke-rpc"));
+        schemaContext = TestRestconfUtils.parseYangSources(testFiles);
+        controllerContext.setSchemas(schemaContext);
+        when(mountPointServiceHandler.get()).thenReturn(mock(DOMMountPointService.class));
+    }
+
+    @Test
+    public void moduleDataTest() throws Exception {
+        final DataSchemaNode dataSchemaNode = schemaContext
+                .getDataChildByName(QName.create(INSTANCE_IDENTIFIER_MODULE_QNAME, "cont"));
+        final YangInstanceIdentifier dataII = YangInstanceIdentifier.of(dataSchemaNode.getQName());
+        final String uri = "instance-identifier-module:cont";
+        mockBodyReader(uri, this.xmlBodyReader, false);
+        final InputStream inputStream = XmlBodyReaderTest.class
+                .getResourceAsStream("/instanceidentifier/xml/xmldata.xml");
+        final NormalizedNodeContext returnValue = this.xmlBodyReader.readFrom(null, null, null, this.mediaType, null,
+                inputStream);
+        checkNormalizedNodeContext(returnValue);
+        checkExpectValueNormalizeNodeContext(dataSchemaNode, returnValue, dataII);
+    }
+
+    @Test
+    public void moduleSubContainerDataPutTest() throws Exception {
+        final DataSchemaNode dataSchemaNode = schemaContext
+                .getDataChildByName(QName.create(INSTANCE_IDENTIFIER_MODULE_QNAME, "cont"));
+        final QName cont1QName = QName.create(dataSchemaNode.getQName(), "cont1");
+        final YangInstanceIdentifier dataII = YangInstanceIdentifier.of(dataSchemaNode.getQName()).node(cont1QName);
+        final DataSchemaNode dataSchemaNodeOnPath = ((DataNodeContainer) dataSchemaNode).getDataChildByName(cont1QName);
+        final String uri = "instance-identifier-module:cont/cont1";
+        mockBodyReader(uri, this.xmlBodyReader, false);
+        final InputStream inputStream = XmlBodyReaderTest.class
+                .getResourceAsStream("/instanceidentifier/xml/xml_sub_container.xml");
+        final NormalizedNodeContext returnValue = this.xmlBodyReader.readFrom(null, null, null, this.mediaType, null,
+                inputStream);
+        checkNormalizedNodeContext(returnValue);
+        checkExpectValueNormalizeNodeContext(dataSchemaNodeOnPath, returnValue, dataII);
+    }
+
+    @Test
+    public void moduleSubContainerDataPostTest() throws Exception {
+        final DataSchemaNode dataSchemaNode = schemaContext
+                .getDataChildByName(QName.create(INSTANCE_IDENTIFIER_MODULE_QNAME, "cont"));
+        final QName cont1QName = QName.create(dataSchemaNode.getQName(), "cont1");
+        final YangInstanceIdentifier dataII = YangInstanceIdentifier.of(dataSchemaNode.getQName()).node(cont1QName);
+        final String uri = "instance-identifier-module:cont";
+        mockBodyReader(uri, this.xmlBodyReader, true);
+        final InputStream inputStream = XmlBodyReaderTest.class
+                .getResourceAsStream("/instanceidentifier/xml/xml_sub_container.xml");
+        final NormalizedNodeContext returnValue = this.xmlBodyReader.readFrom(null, null, null, this.mediaType, null,
+                inputStream);
+        checkNormalizedNodeContext(returnValue);
+        checkExpectValueNormalizeNodeContext(dataSchemaNode, returnValue, dataII);
+    }
+
+    @Test
+    public void moduleSubContainerAugmentDataPostTest() throws Exception {
+        final DataSchemaNode dataSchemaNode = schemaContext
+                .getDataChildByName(QName.create(INSTANCE_IDENTIFIER_MODULE_QNAME, "cont"));
+        final Module augmentModule = schemaContext.findModuleByNamespace(new URI("augment:module")).iterator().next();
+        final QName contAugmentQName = QName.create(augmentModule.getQNameModule(), "cont-augment");
+        final YangInstanceIdentifier.AugmentationIdentifier augII = new YangInstanceIdentifier.AugmentationIdentifier(
+                Sets.newHashSet(contAugmentQName));
+        final YangInstanceIdentifier dataII = YangInstanceIdentifier.of(dataSchemaNode.getQName()).node(augII)
+                .node(contAugmentQName);
+        final String uri = "instance-identifier-module:cont";
+        mockBodyReader(uri, this.xmlBodyReader, true);
+        final InputStream inputStream = XmlBodyReaderTest.class
+                .getResourceAsStream("/instanceidentifier/xml/xml_augment_container.xml");
+        final NormalizedNodeContext returnValue = this.xmlBodyReader.readFrom(null, null, null, this.mediaType, null,
+                inputStream);
+        checkNormalizedNodeContext(returnValue);
+        checkExpectValueNormalizeNodeContext(dataSchemaNode, returnValue, dataII);
+    }
+
+    @Test
+    public void moduleSubContainerChoiceAugmentDataPostTest() throws Exception {
+        final DataSchemaNode dataSchemaNode = schemaContext
+                .getDataChildByName(QName.create(INSTANCE_IDENTIFIER_MODULE_QNAME, "cont"));
+        final Module augmentModule = schemaContext.findModuleByNamespace(new URI("augment:module")).iterator().next();
+        final QName augmentChoice1QName = QName.create(augmentModule.getQNameModule(), "augment-choice1");
+        final QName augmentChoice2QName = QName.create(augmentChoice1QName, "augment-choice2");
+        final QName containerQName = QName.create(augmentChoice1QName, "case-choice-case-container1");
+        final YangInstanceIdentifier.AugmentationIdentifier augChoice1II = new YangInstanceIdentifier.AugmentationIdentifier(
+                Sets.newHashSet(augmentChoice1QName));
+        final YangInstanceIdentifier.AugmentationIdentifier augChoice2II = new YangInstanceIdentifier.AugmentationIdentifier(
+                Sets.newHashSet(augmentChoice2QName));
+        final YangInstanceIdentifier dataII = YangInstanceIdentifier.of(dataSchemaNode.getQName()).node(augChoice1II)
+                .node(augmentChoice1QName).node(augChoice2II).node(augmentChoice2QName).node(containerQName);
+        final String uri = "instance-identifier-module:cont";
+        mockBodyReader(uri, this.xmlBodyReader, true);
+        final InputStream inputStream = XmlBodyReaderTest.class
+                .getResourceAsStream("/instanceidentifier/xml/xml_augment_choice_container.xml");
+        final NormalizedNodeContext returnValue = this.xmlBodyReader.readFrom(null, null, null, this.mediaType, null,
+                inputStream);
+        checkNormalizedNodeContext(returnValue);
+        checkExpectValueNormalizeNodeContext(dataSchemaNode, returnValue, dataII);
+    }
+
+    private void checkExpectValueNormalizeNodeContext(final DataSchemaNode dataSchemaNode,
+            final NormalizedNodeContext nnContext, final YangInstanceIdentifier dataNodeIdent) {
+        assertEquals(dataSchemaNode, nnContext.getInstanceIdentifierContext().getSchemaNode());
+        assertEquals(dataNodeIdent, nnContext.getInstanceIdentifierContext().getInstanceIdentifier());
+        assertNotNull(NormalizedNodes.findNode(nnContext.getData(), dataNodeIdent));
+    }
+
+    /**
+     * Test when container with the same name is placed in two modules
+     * (foo-module and bar-module). Namespace must be used to distinguish
+     * between them to find correct one. Check if container was found not only
+     * according to its name but also by correct namespace used in payload.
+     */
+    @Test
+    public void findFooContainerUsingNamespaceTest() throws Exception {
+        mockBodyReader("", this.xmlBodyReader, true);
+        final InputStream inputStream = XmlBodyReaderTest.class
+                .getResourceAsStream("/instanceidentifier/xml/xmlDataFindFooContainer.xml");
+        final NormalizedNodeContext returnValue = this.xmlBodyReader.readFrom(null, null, null, this.mediaType, null,
+                inputStream);
+
+        // check return value
+        checkNormalizedNodeContext(returnValue);
+        // check if container was found both according to its name and namespace
+        assertEquals("Not correct container found, name was ignored", "foo-bar-container",
+                returnValue.getData().getNodeType().getLocalName());
+        assertEquals("Not correct container found, namespace was ignored", "foo:module",
+                returnValue.getData().getNodeType().getNamespace().toString());
+    }
+
+    /**
+     * Test when container with the same name is placed in two modules
+     * (foo-module and bar-module). Namespace must be used to distinguish
+     * between them to find correct one. Check if container was found not only
+     * according to its name but also by correct namespace used in payload.
+     */
+    @Test
+    public void findBarContainerUsingNamespaceTest() throws Exception {
+        mockBodyReader("", this.xmlBodyReader, true);
+        final InputStream inputStream = XmlBodyReaderTest.class
+                .getResourceAsStream("/instanceidentifier/xml/xmlDataFindBarContainer.xml");
+        final NormalizedNodeContext returnValue = this.xmlBodyReader.readFrom(null, null, null, this.mediaType, null,
+                inputStream);
+
+        // check return value
+        checkNormalizedNodeContext(returnValue);
+        // check if container was found both according to its name and namespace
+        assertEquals("Not correct container found, name was ignored", "foo-bar-container",
+                returnValue.getData().getNodeType().getLocalName());
+        assertEquals("Not correct container found, namespace was ignored", "bar:module",
+                returnValue.getData().getNodeType().getNamespace().toString());
+    }
+}
diff --git a/restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/jersey/providers/XmlPATCHBodyReaderMountPointTest.java b/restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/jersey/providers/XmlPATCHBodyReaderMountPointTest.java
new file mode 100644 (file)
index 0000000..9c6205a
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+ * 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.jersey.providers;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import com.google.common.base.Optional;
+import java.io.InputStream;
+import javax.ws.rs.core.MediaType;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
+import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
+import org.opendaylight.controller.sal.rest.impl.test.providers.TestXmlBodyReader;
+import org.opendaylight.netconf.sal.restconf.impl.PATCHContext;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public class XmlPATCHBodyReaderMountPointTest extends AbstractBodyReaderTest {
+
+    private final XmlToPATCHBodyReader xmlPATCHBodyReader;
+    private static SchemaContext schemaContext;
+    private static final String MOUNT_POINT = "instance-identifier-module:cont/yang-ext:mount/";
+
+    public XmlPATCHBodyReaderMountPointTest() throws Exception {
+        super();
+        xmlPATCHBodyReader = new XmlToPATCHBodyReader();
+    }
+
+    @Override
+    protected MediaType getMediaType() {
+        return new MediaType(MediaType.APPLICATION_XML, null);
+    }
+
+    @BeforeClass
+    public static void initialization() {
+        schemaContext = schemaContextLoader("/instanceidentifier/yang", schemaContext);
+
+        final DOMMountPointService mountPointService = mock(DOMMountPointService.class);
+        final DOMMountPoint mountPoint = mock(DOMMountPoint.class);
+
+        when(mountPointServiceHandler.get()).thenReturn(mountPointService);
+        when(mountPointService.getMountPoint(any(YangInstanceIdentifier.class))).thenReturn(Optional.of(mountPoint));
+        when(mountPoint.getSchemaContext()).thenReturn(schemaContext);
+
+        controllerContext.setSchemas(schemaContext);
+    }
+
+    @Test
+    public void moduleDataTest() throws Exception {
+        final String uri = MOUNT_POINT + "instance-identifier-patch-module:patch-cont/my-list1=leaf1";
+        mockBodyReader(uri, xmlPATCHBodyReader, false);
+        final InputStream inputStream = TestXmlBodyReader.class
+                .getResourceAsStream("/instanceidentifier/xml/xmlPATCHdata.xml");
+        final PATCHContext returnValue = xmlPATCHBodyReader
+                .readFrom(null, null, null, mediaType, null, inputStream);
+        checkPATCHContextMountPoint(returnValue);
+    }
+
+    /**
+     * Test trying to use PATCH create operation which requires value without value. Error code 400 should be returned.
+     */
+    @Test
+    public void moduleDataValueMissingNegativeTest() throws Exception {
+        final String uri = MOUNT_POINT + "instance-identifier-patch-module:patch-cont/my-list1=leaf1";
+        mockBodyReader(uri, xmlPATCHBodyReader, false);
+        final InputStream inputStream = TestXmlBodyReader.class
+                .getResourceAsStream("/instanceidentifier/xml/xmlPATCHdataValueMissing.xml");
+        try {
+            xmlPATCHBodyReader.readFrom(null, null, null, mediaType, null, inputStream);
+            fail("Test should return error 400 due to missing value node when attempt to invoke create operation");
+        } catch (final RestconfDocumentedException e) {
+            assertEquals("Error code 400 expected", 400, e.getErrors().get(0).getErrorTag().getStatusCode());
+        }
+    }
+
+    /**
+     * Test trying to use value with PATCH delete operation which does not support value. Error code 400 should be
+     * returned.
+     */
+    @Test
+    public void moduleDataNotValueNotSupportedNegativeTest() throws Exception {
+        final String uri = MOUNT_POINT + "instance-identifier-patch-module:patch-cont/my-list1=leaf1";
+        mockBodyReader(uri, xmlPATCHBodyReader, false);
+        final InputStream inputStream = TestXmlBodyReader.class
+                .getResourceAsStream("/instanceidentifier/xml/xmlPATCHdataValueNotSupported.xml");
+        try {
+            xmlPATCHBodyReader.readFrom(null, null, null, mediaType, null, inputStream);
+            fail("Test should return error 400 due to present value node when attempt to invoke delete operation");
+        } catch (final RestconfDocumentedException e) {
+            assertEquals("Error code 400 expected", 400, e.getErrors().get(0).getErrorTag().getStatusCode());
+        }
+    }
+
+
+    /**
+     * Test of Yang PATCH with absolute target path.
+     */
+    @Test
+    public void moduleDataAbsoluteTargetPathTest() throws Exception {
+        final String uri = MOUNT_POINT;
+        mockBodyReader(uri, xmlPATCHBodyReader, false);
+        final InputStream inputStream = TestXmlBodyReader.class
+                .getResourceAsStream("/instanceidentifier/xml/xmlPATCHdataAbsoluteTargetPath.xml");
+        final PATCHContext returnValue = xmlPATCHBodyReader
+                .readFrom(null, null, null, mediaType, null, inputStream);
+        checkPATCHContextMountPoint(returnValue);
+    }
+
+    /**
+     * Test using PATCH when target is completely specified in request URI and thus target leaf contains only '/' sign.
+     */
+    @Test
+    public void modulePATCHCompleteTargetInURITest() throws Exception {
+        final String uri = MOUNT_POINT + "instance-identifier-patch-module:patch-cont";
+        mockBodyReader(uri, xmlPATCHBodyReader, false);
+        final InputStream inputStream = TestXmlBodyReader.class
+                .getResourceAsStream("/instanceidentifier/xml/xmlPATCHdataCompleteTargetInURI.xml");
+        final PATCHContext returnValue = xmlPATCHBodyReader
+                .readFrom(null, null, null, mediaType, null, inputStream);
+        checkPATCHContextMountPoint(returnValue);
+    }
+
+    /**
+     * Test of Yang PATCH merge operation on list. Test consists of two edit operations - replace and merge.
+     */
+    @Test
+    public void moduleDataMergeOperationOnListTest() throws Exception {
+        final String uri = MOUNT_POINT + "instance-identifier-patch-module:patch-cont/my-list1=leaf1";
+        mockBodyReader(uri, xmlPATCHBodyReader, false);
+        final InputStream inputStream = TestXmlBodyReader.class
+                .getResourceAsStream("/instanceidentifier/xml/xmlPATCHdataMergeOperationOnList.xml");
+        final PATCHContext returnValue = xmlPATCHBodyReader
+                .readFrom(null, null, null, mediaType, null, inputStream);
+        checkPATCHContextMountPoint(returnValue);
+    }
+
+    /**
+     * Test of Yang PATCH merge operation on container. Test consists of two edit operations - create and merge.
+     */
+    @Test
+    public void moduleDataMergeOperationOnContainerTest() throws Exception {
+        final String uri = MOUNT_POINT + "instance-identifier-patch-module:patch-cont";
+        mockBodyReader(uri, xmlPATCHBodyReader, false);
+        final InputStream inputStream = TestXmlBodyReader.class
+                .getResourceAsStream("/instanceidentifier/xml/xmlPATCHdataMergeOperationOnContainer.xml");
+        final PATCHContext returnValue = xmlPATCHBodyReader
+                .readFrom(null, null, null, mediaType, null, inputStream);
+        checkPATCHContextMountPoint(returnValue);
+    }
+}
@@ -6,26 +6,29 @@
  * and is available at http://www.eclipse.org/legal/epl-v10.html
  */
 
-package org.opendaylight.controller.sal.rest.impl.test.providers;
+package org.opendaylight.restconf.jersey.providers;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
 
 import java.io.InputStream;
 import javax.ws.rs.core.MediaType;
 import org.junit.BeforeClass;
 import org.junit.Test;
+import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
+import org.opendaylight.controller.sal.rest.impl.test.providers.TestXmlBodyReader;
 import org.opendaylight.netconf.sal.restconf.impl.PATCHContext;
 import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
-import org.opendaylight.restconf.jersey.providers.XmlToPATCHBodyReader;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 
-public class TestDraft17XmlPATCHBodyReader extends Draft17AbstractBodyReaderTest {
+public class XmlPATCHBodyReaderTest extends AbstractBodyReaderTest {
 
     private final XmlToPATCHBodyReader xmlPATCHBodyReader;
     private static SchemaContext schemaContext;
 
-    public TestDraft17XmlPATCHBodyReader() throws NoSuchFieldException, SecurityException {
+    public XmlPATCHBodyReaderTest() throws Exception {
         super();
         xmlPATCHBodyReader = new XmlToPATCHBodyReader();
     }
@@ -36,8 +39,9 @@ public class TestDraft17XmlPATCHBodyReader extends Draft17AbstractBodyReaderTest
     }
 
     @BeforeClass
-    public static void initialization() throws NoSuchFieldException, SecurityException {
+    public static void initialization() {
         schemaContext = schemaContextLoader("/instanceidentifier/yang", schemaContext);
+        when(mountPointServiceHandler.get()).thenReturn(mock(DOMMountPointService.class));
         controllerContext.setSchemas(schemaContext);
     }
 
index 51f5c895d81eae3361ba560f29f83fba28154232..bf727d439b105fd371c69ec6ec151d3d5505d5ed 100644 (file)
@@ -11,6 +11,7 @@ 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.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Matchers.any;
 import static org.mockito.Mockito.doNothing;
@@ -354,6 +355,35 @@ public class RestconfDataServiceImplTest {
         assertEquals("replace data", status.getEditCollection().get(1).getEditId());
     }
 
+    @Test
+    public void testPatchDataMountPoint() throws Exception {
+        final InstanceIdentifierContext<? extends SchemaNode> iidContext = new InstanceIdentifierContext<>(
+                iidBase, schemaNode, mountPoint, contextRef.get());
+        final List<PATCHEntity> entity = new ArrayList<>();
+        final YangInstanceIdentifier iidleaf = YangInstanceIdentifier.builder(iidBase)
+                .node(containerPlayerQname)
+                .node(leafQname)
+                .build();
+        entity.add(new PATCHEntity("create data", "CREATE", iidBase, buildBaseCont));
+        entity.add(new PATCHEntity("replace data", "REPLACE", iidBase, buildBaseCont));
+        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());
+        assertNull(status.getGlobalErrors());
+    }
+
     @Test
     public void testPatchDataDeleteNotExist() throws Exception {
         final Field handler = RestConnectorProvider.class.getDeclaredField("transactionChainHandler");