Test Json/Xml PatchBodyReaders on mixin nodes 83/103483/31
authorOleksandrZharov <Oleksandr.Zharov@pantheon.tech>
Fri, 26 Aug 2022 15:43:24 +0000 (17:43 +0200)
committerRobert Varga <nite@hq.sk>
Tue, 24 Jan 2023 10:32:03 +0000 (10:32 +0000)
Created tests for both JSON and XML PatchBodyReaders with
different types of mixin nodes.

JIRA: NETCONF-943
Change-Id: Idd9bc2da6d561463bdd01b9714ea9f61a7a86e88
Signed-off-by: OleksandrZharov <Oleksandr.Zharov@pantheon.tech>
restconf/restconf-nb/src/test/java/org/opendaylight/restconf/nb/rfc8040/jersey/providers/patch/JsonPatchBodyReaderTest.java
restconf/restconf-nb/src/test/java/org/opendaylight/restconf/nb/rfc8040/jersey/providers/patch/XmlPatchBodyReaderTest.java
restconf/restconf-nb/src/test/java/org/opendaylight/restconf/nb/rfc8040/jersey/providers/test/AbstractBodyReaderTest.java
restconf/restconf-nb/src/test/resources/instanceidentifier/yang/choice-model.yang [new file with mode: 0644]
restconf/restconf-nb/src/test/resources/instanceidentifier/yang/leaf-set-model.yang [new file with mode: 0644]
restconf/restconf-nb/src/test/resources/instanceidentifier/yang/map-model.yang [new file with mode: 0644]
restconf/restconf-nb/src/test/resources/instanceidentifier/yang/unkeyed-list-model.yang [new file with mode: 0644]

index 7c557b1dd744625264c4f0d98a65626b4e115f69..664ae5e79a1c3de20d5aa7bbdcbbde898b864796 100644 (file)
@@ -11,7 +11,9 @@ import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertThrows;
 
+import java.io.ByteArrayInputStream;
 import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
 import javax.ws.rs.core.MediaType;
 import org.junit.BeforeClass;
 import org.junit.Test;
@@ -20,6 +22,11 @@ import org.opendaylight.restconf.common.patch.PatchContext;
 import org.opendaylight.restconf.nb.rfc8040.jersey.providers.test.AbstractBodyReaderTest;
 import org.opendaylight.restconf.nb.rfc8040.jersey.providers.test.JsonBodyReaderTest;
 import org.opendaylight.yangtools.yang.common.ErrorTag;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
 
 public class JsonPatchBodyReaderTest extends AbstractBodyReaderTest {
@@ -177,4 +184,163 @@ public class JsonPatchBodyReaderTest extends AbstractBodyReaderTest {
         final PatchContext returnValue = jsonToPatchBodyReader.readFrom(null, null, null, mediaType, null, inputStream);
         checkPatchContext(returnValue);
     }
+
+    /**
+     * Test of Yang Patch on the system map node element.
+     */
+    @Test
+    public void modulePatchTargetMapNodeTest() throws Exception {
+        mockBodyReader("", jsonToPatchBodyReader, false);
+        final var inputStream = new ByteArrayInputStream("""
+            {
+                "ietf-yang-patch:yang-patch": {
+                    "patch-id": "map-patch",
+                    "comment": "comment",
+                    "edit": [
+                        {
+                            "edit-id": "edit1",
+                            "operation": "replace",
+                            "target": "/map-model:cont-root/map-model:cont1/map-model:my-map=key",
+                            "value": {
+                                "my-map": {
+                                    "key-leaf": "key",
+                                    "data-leaf": "data"
+                                }
+                            }
+                        }
+                    ]
+                }
+            }
+            """.getBytes(StandardCharsets.UTF_8));
+        final var expectedData = Builders.mapBuilder()
+                .withNodeIdentifier(new NodeIdentifier(MAP_CONT_QNAME))
+                .withChild(Builders.mapEntryBuilder()
+                        .withNodeIdentifier(NodeIdentifierWithPredicates.of(MAP_CONT_QNAME, KEY_LEAF_QNAME, "key"))
+                        .withChild(ImmutableNodes.leafNode(KEY_LEAF_QNAME, "key"))
+                        .withChild(ImmutableNodes.leafNode(DATA_LEAF_QNAME, "data"))
+                        .build())
+                .build();
+        final var returnValue = jsonToPatchBodyReader.readFrom(null, null, null, mediaType, null, inputStream);
+        checkPatchContext(returnValue);
+        final var data = returnValue.getData().get(0).getNode();
+        assertEquals(MAP_CONT_QNAME, data.getIdentifier().getNodeType());
+        assertEquals(expectedData, data);
+    }
+
+    /**
+     * Test of Yang Patch on the leaf set node element.
+     */
+    @Test
+    public void modulePatchTargetLeafSetNodeTest() throws Exception {
+        mockBodyReader("", jsonToPatchBodyReader, false);
+        final var inputStream = new ByteArrayInputStream("""
+            {
+                "ietf-yang-patch:yang-patch": {
+                    "patch-id": "set-patch",
+                    "comment": "comment",
+                    "edit": [
+                        {
+                            "edit-id": "edit1",
+                            "operation": "replace",
+                            "target": "/set-model:cont-root/set-model:cont1/set-model:my-set=data1",
+                            "value": {
+                                "my-set": [ "data1" ]
+                            }
+                        }
+                    ]
+                }
+            }
+            """.getBytes(StandardCharsets.UTF_8));
+        final var expectedData = Builders.leafSetBuilder()
+                .withNodeIdentifier(new NodeIdentifier(LEAF_SET_QNAME))
+                .withChild(Builders.leafSetEntryBuilder()
+                        .withNodeIdentifier(new NodeWithValue(LEAF_SET_QNAME, "data1"))
+                        .withValue("data1")
+                        .build())
+                .build();
+        final var returnValue = jsonToPatchBodyReader.readFrom(null, null, null, mediaType, null, inputStream);
+        checkPatchContext(returnValue);
+        final var data = returnValue.getData().get(0).getNode();
+        assertEquals(LEAF_SET_QNAME, data.getIdentifier().getNodeType());
+        assertEquals(expectedData, data);
+    }
+
+    /**
+     * Test of Yang Patch on the unkeyed list node element.
+     */
+    @Test
+    public void modulePatchTargetUnkeyedListNodeTest() throws Exception {
+        mockBodyReader("", jsonToPatchBodyReader, false);
+        final var inputStream = new ByteArrayInputStream("""
+            {
+                "ietf-yang-patch:yang-patch": {
+                    "patch-id": "list-patch",
+                    "comment": "comment",
+                    "edit": [
+                        {
+                            "edit-id": "edit1",
+                            "operation": "replace",
+                            "target": "/list-model:cont-root/list-model:cont1/list-model:unkeyed-list",
+                            "value": {
+                                "unkeyed-list": {
+                                    "leaf1": "data1",
+                                    "leaf2": "data2"
+                                }
+                            }
+                        }
+                    ]
+                }
+            }
+            """.getBytes(StandardCharsets.UTF_8));
+        final var expectedData = Builders.unkeyedListBuilder()
+                .withNodeIdentifier(new NodeIdentifier(LIST_QNAME))
+                .withChild(Builders.unkeyedListEntryBuilder()
+                        .withNodeIdentifier(new NodeIdentifier(LIST_QNAME))
+                        .withChild(ImmutableNodes.leafNode(LIST_LEAF1_QNAME, "data1"))
+                        .withChild(ImmutableNodes.leafNode(LIST_LEAF2_QNAME, "data2"))
+                        .build())
+                .build();
+        final var returnValue = jsonToPatchBodyReader.readFrom(null, null, null, mediaType, null, inputStream);
+        checkPatchContext(returnValue);
+        final var data = returnValue.getData().get(0).getNode();
+        assertEquals(LIST_QNAME, data.getIdentifier().getNodeType());
+        assertEquals(expectedData, data);
+    }
+
+    /**
+     * Test of Yang Patch on the case node element.
+     */
+    @Test
+    public void modulePatchTargetCaseNodeTest() throws Exception {
+        mockBodyReader("", jsonToPatchBodyReader, false);
+        final var inputStream = new ByteArrayInputStream("""
+            {
+                "ietf-yang-patch:yang-patch": {
+                    "patch-id": "choice-patch",
+                    "comment": "comment",
+                    "edit": [
+                        {
+                            "edit-id": "edit1",
+                            "operation": "replace",
+                            "target": "/choice-model:cont-root/choice-model:cont1/choice-model:case-cont1",
+                            "value": {
+                                "case-cont1": {
+                                    "case-leaf1": "data"
+                                }
+                            }
+                        }
+                    ]
+                }
+            }
+            """.getBytes(StandardCharsets.UTF_8));
+        final var expectedData = Builders.containerBuilder()
+                .withNodeIdentifier(new NodeIdentifier(CHOICE_CONT_QNAME))
+                .withChild(ImmutableNodes.leafNode(CASE_LEAF1_QNAME, "data"))
+                .build();
+        final var returnValue = jsonToPatchBodyReader.readFrom(null, null, null, mediaType, null, inputStream);
+        checkPatchContext(returnValue);
+        final var data = returnValue.getData().get(0).getNode();
+        assertEquals(CHOICE_CONT_QNAME, data.getIdentifier().getNodeType());
+        assertEquals(expectedData, data);
+    }
 }
index 71a7bee0bf61dc0668f631424f55ea4c537a25df..c9b33b2062a3921439a211d88f8b2d0277bc2af0 100644 (file)
@@ -10,14 +10,22 @@ package org.opendaylight.restconf.nb.rfc8040.jersey.providers.patch;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertThrows;
 
+import java.io.ByteArrayInputStream;
 import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
 import javax.ws.rs.core.MediaType;
 import org.junit.BeforeClass;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
 import org.opendaylight.restconf.nb.rfc8040.jersey.providers.test.AbstractBodyReaderTest;
 import org.opendaylight.restconf.nb.rfc8040.jersey.providers.test.XmlBodyReaderTest;
 import org.opendaylight.yangtools.yang.common.ErrorTag;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
 
 public class XmlPatchBodyReaderTest extends AbstractBodyReaderTest {
@@ -127,4 +135,149 @@ public class XmlPatchBodyReaderTest extends AbstractBodyReaderTest {
                 .getResourceAsStream("/instanceidentifier/xml/xmlPATCHTargetTopLevelContainerWithEmptyURI.xml");
         checkPatchContext(xmlToPatchBodyReader.readFrom(null, null, null, mediaType, null, inputStream));
     }
+
+    /**
+     * Test of Yang Patch on the top system map node element.
+     */
+    @Test
+    public void moduleTargetMapNodeTest() throws Exception {
+        mockBodyReader("", xmlToPatchBodyReader, false);
+        final var inputStream = new ByteArrayInputStream("""
+            <yang-patch xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-patch">
+                <patch-id>map-patch</patch-id>
+                <comment>YANG patch comment</comment>
+                <edit>
+                    <edit-id>edit1</edit-id>
+                    <operation>replace</operation>
+                    <target>/map-model:cont-root/map-model:cont1/map-model:my-map=key</target>
+                    <value>
+                        <my-map xmlns="map:ns">
+                            <key-leaf>key</key-leaf>
+                            <data-leaf>data</data-leaf>
+                        </my-map>
+                    </value>
+                </edit>
+            </yang-patch>
+            """.getBytes(StandardCharsets.UTF_8));
+        final var expectedData = Builders.mapBuilder()
+                .withNodeIdentifier(new NodeIdentifier(MAP_CONT_QNAME))
+                .withChild(Builders.mapEntryBuilder()
+                        .withNodeIdentifier(NodeIdentifierWithPredicates.of(MAP_CONT_QNAME, KEY_LEAF_QNAME, "key"))
+                        .withChild(ImmutableNodes.leafNode(KEY_LEAF_QNAME, "key"))
+                        .withChild(ImmutableNodes.leafNode(DATA_LEAF_QNAME, "data"))
+                        .build())
+                .build();
+        final var returnValue = xmlToPatchBodyReader.readFrom(null, null, null, mediaType, null, inputStream);
+        checkPatchContext(returnValue);
+        final var data = returnValue.getData().get(0).getNode();
+        assertEquals(MAP_CONT_QNAME, data.getIdentifier().getNodeType());
+        assertEquals(expectedData, data);
+    }
+
+    /**
+     * Test of Yang Patch on the leaf set node element.
+     * TODO: Remove ignore when NETCONF-937 will be resolved
+     */
+    @Ignore
+    @Test
+    public void modulePatchTargetLeafSetNodeTest() throws Exception {
+        mockBodyReader("", xmlToPatchBodyReader, false);
+        final var inputStream = new ByteArrayInputStream("""
+            <yang-patch xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-patch">
+                <patch-id>set-patch</patch-id>
+                <comment>YANG patch comment</comment>
+                <edit>
+                    <edit-id>edit1</edit-id>
+                    <operation>replace</operation>
+                    <target>/set-model:cont-root/set-model:cont1/set-model:my-set="data1"</target>
+                    <value>
+                        <my-set xmlns="set:ns">data1</my-set>
+                    </value>
+                </edit>
+            </yang-patch>
+            """.getBytes(StandardCharsets.UTF_8));
+        final var expectedData = Builders.leafSetBuilder()
+                .withNodeIdentifier(new NodeIdentifier(LEAF_SET_QNAME))
+                .withChild(Builders.leafSetEntryBuilder()
+                        .withNodeIdentifier(new NodeWithValue(LEAF_SET_QNAME, "data1"))
+                        .withValue("data1")
+                        .build())
+                .build();
+        final var returnValue = xmlToPatchBodyReader.readFrom(null, null, null, mediaType, null, inputStream);
+        checkPatchContext(returnValue);
+        final var data = returnValue.getData().get(0).getNode();
+        assertEquals(LEAF_SET_QNAME, data.getIdentifier().getNodeType());
+        assertEquals(expectedData, data);
+    }
+
+    /**
+     * Test of Yang Patch on the top unkeyed list element.
+     */
+    @Test
+    public void moduleTargetUnkeyedListNodeTest() throws Exception {
+        mockBodyReader("", xmlToPatchBodyReader, false);
+        final var inputStream = new ByteArrayInputStream("""
+            <yang-patch xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-patch">
+                <patch-id>list-patch</patch-id>
+                <comment>YANG patch comment</comment>
+                <edit>
+                    <edit-id>edit1</edit-id>
+                    <operation>replace</operation>
+                    <target>/list-model:cont-root/list-model:cont1/list-model:unkeyed-list</target>
+                    <value>
+                        <unkeyed-list xmlns="list:ns">
+                            <leaf1>data1</leaf1>
+                            <leaf2>data2</leaf2>
+                        </unkeyed-list>
+                    </value>
+                </edit>
+            </yang-patch>
+            """.getBytes(StandardCharsets.UTF_8));
+        final var expectedData = Builders.unkeyedListBuilder()
+                .withNodeIdentifier(new NodeIdentifier(LIST_QNAME))
+                .withChild(Builders.unkeyedListEntryBuilder()
+                        .withNodeIdentifier(new NodeIdentifier(LIST_QNAME))
+                        .withChild(ImmutableNodes.leafNode(LIST_LEAF1_QNAME, "data1"))
+                        .withChild(ImmutableNodes.leafNode(LIST_LEAF2_QNAME, "data2"))
+                        .build())
+                .build();
+        final var returnValue = xmlToPatchBodyReader.readFrom(null, null, null, mediaType, null, inputStream);
+        checkPatchContext(returnValue);
+        final var data = returnValue.getData().get(0).getNode();
+        assertEquals(LIST_QNAME, data.getIdentifier().getNodeType());
+        assertEquals(expectedData, data);
+    }
+
+    /**
+     * Test of Yang Patch on the top case node element.
+     */
+    @Test
+    public void moduleTargetCaseNodeTest() throws Exception {
+        mockBodyReader("", xmlToPatchBodyReader, false);
+        final var inputStream = new ByteArrayInputStream("""
+            <yang-patch xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-patch">
+                <patch-id>choice-patch</patch-id>
+                <comment>YANG patch comment</comment>
+                <edit>
+                    <edit-id>edit1</edit-id>
+                    <operation>replace</operation>
+                    <target>/choice-model:cont-root/choice-model:cont1/choice-model:case-cont1</target>
+                    <value>
+                        <case-cont1 xmlns="choice:ns">
+                            <case-leaf1>data</case-leaf1>
+                        </case-cont1>
+                    </value>
+                </edit>
+            </yang-patch>
+            """.getBytes(StandardCharsets.UTF_8));
+        final var expectedData = Builders.containerBuilder()
+                .withNodeIdentifier(new NodeIdentifier(CHOICE_CONT_QNAME))
+                .withChild(ImmutableNodes.leafNode(CASE_LEAF1_QNAME, "data"))
+                .build();
+        final var returnValue = xmlToPatchBodyReader.readFrom(null, null, null, mediaType, null, inputStream);
+        checkPatchContext(returnValue);
+        final var data = returnValue.getData().get(0).getNode();
+        assertEquals(CHOICE_CONT_QNAME, data.getIdentifier().getNodeType());
+        assertEquals(expectedData, data);
+    }
 }
index f9aa84238de09b675e05da19a366a29bc6c20688..876dbfa0029176906832e60458c28f76d4d17e30 100644 (file)
@@ -38,10 +38,21 @@ import org.opendaylight.restconf.nb.rfc8040.jersey.providers.spi.AbstractIdentif
 import org.opendaylight.restconf.nb.rfc8040.legacy.NormalizedNodePayload;
 import org.opendaylight.yangtools.yang.common.ErrorTag;
 import org.opendaylight.yangtools.yang.common.ErrorType;
+import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
 
 public abstract class AbstractBodyReaderTest {
+    protected static final QName MAP_CONT_QNAME = QName.create("map:ns", "my-map").intern();
+    protected static final QName KEY_LEAF_QNAME = QName.create("map:ns", "key-leaf").intern();
+    protected static final QName DATA_LEAF_QNAME = QName.create("map:ns", "data-leaf").intern();
+    protected static final QName LEAF_SET_QNAME = QName.create("set:ns", "my-set").intern();
+    protected static final QName LIST_QNAME = QName.create("list:ns", "unkeyed-list").intern();
+    protected static final QName LIST_LEAF1_QNAME = QName.create("list:ns", "leaf1").intern();
+    protected static final QName LIST_LEAF2_QNAME = QName.create("list:ns", "leaf2").intern();
+    protected static final QName CHOICE_CONT_QNAME = QName.create("choice:ns", "case-cont1").intern();
+    protected static final QName CASE_LEAF1_QNAME = QName.create("choice:ns", "case-leaf1").intern();
+
     protected final MediaType mediaType;
     protected final DatabindProvider databindProvider;
     protected final DOMMountPointService mountPointService;
diff --git a/restconf/restconf-nb/src/test/resources/instanceidentifier/yang/choice-model.yang b/restconf/restconf-nb/src/test/resources/instanceidentifier/yang/choice-model.yang
new file mode 100644 (file)
index 0000000..aeee9f5
--- /dev/null
@@ -0,0 +1,18 @@
+module choice-model {
+  namespace "choice:ns";
+  prefix choice-model;
+
+  container cont-root {
+    container cont1 {
+      choice choice1 {
+        case case1 {
+          container case-cont1 {
+            leaf case-leaf1 {
+              type string;
+            }
+          }
+        }
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/restconf/restconf-nb/src/test/resources/instanceidentifier/yang/leaf-set-model.yang b/restconf/restconf-nb/src/test/resources/instanceidentifier/yang/leaf-set-model.yang
new file mode 100644 (file)
index 0000000..cc35fc6
--- /dev/null
@@ -0,0 +1,12 @@
+module set-model {
+  namespace "set:ns";
+  prefix set-model;
+
+  container cont-root {
+    container cont1 {
+      leaf-list my-set {
+        type string;
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/restconf/restconf-nb/src/test/resources/instanceidentifier/yang/map-model.yang b/restconf/restconf-nb/src/test/resources/instanceidentifier/yang/map-model.yang
new file mode 100644 (file)
index 0000000..611efae
--- /dev/null
@@ -0,0 +1,21 @@
+module map-model {
+  namespace "map:ns";
+  prefix map-model;
+
+  container cont-root {
+    container cont1 {
+      list my-map {
+
+        key "key-leaf";
+
+        leaf key-leaf {
+          type string;
+        }
+
+        leaf data-leaf {
+          type string;
+        }
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/restconf/restconf-nb/src/test/resources/instanceidentifier/yang/unkeyed-list-model.yang b/restconf/restconf-nb/src/test/resources/instanceidentifier/yang/unkeyed-list-model.yang
new file mode 100644 (file)
index 0000000..ea47570
--- /dev/null
@@ -0,0 +1,17 @@
+module list-model {
+  namespace "list:ns";
+  prefix list-model;
+
+  container cont-root {
+    container cont1 {
+      list unkeyed-list {
+        leaf leaf1 {
+          type string;
+        }
+        leaf leaf2 {
+          type string;
+        }
+      }
+    }
+  }
+}
\ No newline at end of file