Unit test for PutDataTransatcionUtil class 16/44716/13
authormiroslav.kovac <miroslav.kovac@pantheon.tech>
Fri, 26 Aug 2016 09:36:37 +0000 (11:36 +0200)
committerMiroslav Kovac <miroslav.kovac@pantheon.tech>
Mon, 5 Sep 2016 09:00:55 +0000 (09:00 +0000)
Change-Id: Iccdfc3423acb52b6efdb3981985cbe7713a8fae6
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/TransactionUtil.java
restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/restful/utils/PutDataTransactionUtilTest.java [new file with mode: 0644]
restconf/sal-rest-connector/src/test/resources/jukebox/example-jukebox@2015-04-04.yang [new file with mode: 0644]

index 2d6d5fb16e39dc3ff8aa4e47f559cbf2bb58a27b..23fe6fd7306a7dca5430a65a07c1d6bff0f0d9ce 100644 (file)
@@ -87,6 +87,7 @@ public class RestconfDataServiceImpl implements RestconfDataService {
         final String etag = '"' + node.getNodeType().getModule().getFormattedRevision()
                 + node.getNodeType().getLocalName() + '"';
         Response resp = null;
+
         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();
index 2cd6899e3290b34289efa4b5d08cbc1f743e8ed1..4758f707f34834ac0c32e09e15bf6b4ad37631b6 100644 (file)
@@ -26,8 +26,6 @@ import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
-import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.Module;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -57,35 +55,30 @@ public final class TransactionUtil {
     public static void ensureParentsByMerge(final YangInstanceIdentifier path, final SchemaContext schemaContext,
             final DOMDataWriteTransaction writeTx) {
         final List<PathArgument> normalizedPathWithoutChildArgs = new ArrayList<>();
-        boolean hasList = false;
         YangInstanceIdentifier rootNormalizedPath = null;
 
         final Iterator<PathArgument> it = path.getPathArguments().iterator();
-        final Module module = schemaContext.findModuleByNamespaceAndRevision(
-                path.getLastPathArgument().getNodeType().getModule().getNamespace(),
-                path.getLastPathArgument().getNodeType().getModule().getRevision());
 
         while (it.hasNext()) {
             final PathArgument pathArgument = it.next();
             if (rootNormalizedPath == null) {
                 rootNormalizedPath = YangInstanceIdentifier.create(pathArgument);
             }
+
             if (it.hasNext()) {
                 normalizedPathWithoutChildArgs.add(pathArgument);
-                if (module.getDataChildByName(pathArgument.getNodeType()) instanceof ListSchemaNode) {
-                    hasList = true;
-                }
             }
         }
+
         if (normalizedPathWithoutChildArgs.isEmpty()) {
             return;
         }
-        if (hasList) {
-            Preconditions.checkArgument(rootNormalizedPath != null, "Empty path received");
-            final NormalizedNode<?, ?> parentStructure = ImmutableNodes.fromInstanceId(schemaContext,
-                    YangInstanceIdentifier.create(normalizedPathWithoutChildArgs));
-            writeTx.merge(LogicalDatastoreType.CONFIGURATION, rootNormalizedPath, parentStructure);
-        }
+
+        Preconditions.checkArgument(rootNormalizedPath != null, "Empty path received");
+
+        final NormalizedNode<?, ?> parentStructure = ImmutableNodes.fromInstanceId(schemaContext,
+                YangInstanceIdentifier.create(normalizedPathWithoutChildArgs));
+        writeTx.merge(LogicalDatastoreType.CONFIGURATION, rootNormalizedPath, parentStructure);
     }
 
     /**
diff --git a/restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/restful/utils/PutDataTransactionUtilTest.java b/restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/restful/utils/PutDataTransactionUtilTest.java
new file mode 100644 (file)
index 0000000..21bdfd0
--- /dev/null
@@ -0,0 +1,254 @@
+/*
+ * 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.utils;
+
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
+
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.Futures;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+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.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.RestconfDocumentedException;
+import org.opendaylight.restconf.common.references.SchemaContextRef;
+import org.opendaylight.restconf.restful.transaction.TransactionVarsWrapper;
+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.SchemaContext;
+
+public class PutDataTransactionUtilTest {
+
+    private static final String PATH_FOR_NEW_SCHEMA_CONTEXT = "/jukebox";
+
+    @Mock
+    private DOMTransactionChain transactionChain;
+    @Mock
+    private DOMDataReadWriteTransaction readWrite;
+    @Mock
+    private DOMDataReadOnlyTransaction read;
+    @Mock
+    private DOMDataWriteTransaction write;
+
+
+    private SchemaContextRef refSchemaCtx;
+    private LeafNode buildLeaf;
+    private ContainerNode buildBaseCont;
+    private ContainerNode buildBaseContWithList;
+    private MapEntryNode buildListEntry;
+    private SchemaContext schema;
+    private DataSchemaNode schemaNode;
+    private YangInstanceIdentifier iid;
+    private DataSchemaNode schemaNode2;
+    private YangInstanceIdentifier iid2;
+    private DataSchemaNode schemaNode3;
+    private YangInstanceIdentifier iid3;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        refSchemaCtx = new SchemaContextRef(TestRestconfUtils.loadSchemaContext(PATH_FOR_NEW_SCHEMA_CONTEXT));
+        schema = refSchemaCtx.get();
+
+        final QName baseQName = QName.create("http://example.com/ns/example-jukebox", "2015-04-04", "jukebox");
+        final QName containerQname = QName.create(baseQName, "player");
+        final QName leafQname = QName.create(baseQName, "gap");
+        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 YangInstanceIdentifier.NodeIdentifierWithPredicates nodeWithKey2 =
+                new YangInstanceIdentifier.NodeIdentifierWithPredicates(listQname, listKeyQname, "name of band 2");
+
+        iid = YangInstanceIdentifier.builder()
+                .node(baseQName)
+                .node(containerQname)
+                .node(leafQname)
+                .build();
+        schemaNode = DataSchemaContextTree.from(schema).getChild(iid).getDataSchemaNode();
+
+        iid2 = YangInstanceIdentifier.builder()
+                .node(baseQName)
+                .build();
+        schemaNode2 = DataSchemaContextTree.from(schema).getChild(iid2).getDataSchemaNode();
+
+        iid3 = YangInstanceIdentifier.builder()
+                .node(baseQName)
+                .node(listQname)
+                .node(nodeWithKey)
+                .build();
+        schemaNode3 = DataSchemaContextTree.from(schema).getChild(iid3).getDataSchemaNode();
+
+        buildLeaf = Builders.leafBuilder()
+                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(leafQname))
+                .withValue(0.2)
+                .build();
+        final ContainerNode buildPlayerCont = Builders.containerBuilder()
+                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(containerQname))
+                .withChild(buildLeaf)
+                .build();
+        buildBaseCont = Builders.containerBuilder()
+                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(baseQName))
+                .withChild(buildPlayerCont)
+                .build();
+        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();
+        buildListEntry = Builders.mapEntryBuilder()
+                .withNodeIdentifier(nodeWithKey)
+                .withChild(content)
+                .withChild(content2)
+                .build();
+        final LeafNode<Object> content3 = Builders.leafBuilder()
+                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(QName.create(baseQName, "name")))
+                .withValue("name of band 2")
+                .build();
+        final LeafNode<Object> content4 = Builders.leafBuilder()
+                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(QName.create(baseQName, "description")))
+                .withValue("band description 2")
+                .build();
+        final MapEntryNode buildListEntry2 = Builders.mapEntryBuilder()
+                .withNodeIdentifier(nodeWithKey2)
+                .withChild(content3)
+                .withChild(content4)
+                .build();
+        final MapNode buildList = Builders.mapBuilder()
+                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(listQname))
+                .withChild(buildListEntry)
+                .withChild(buildListEntry2)
+                .build();
+        buildBaseContWithList = Builders.containerBuilder()
+                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(baseQName))
+                .withChild(buildList)
+                .build();
+
+    }
+
+    @Test
+    public void testValidInputData() throws Exception {
+        final InstanceIdentifierContext<DataSchemaNode> iidContext = new InstanceIdentifierContext<>(iid, schemaNode, null, schema);
+        final NormalizedNodeContext payload = new NormalizedNodeContext(iidContext, buildLeaf);
+        PutDataTransactionUtil.validInputData(iidContext.getSchemaNode(), payload);
+    }
+
+    @Test
+    public void testValidTopLevelNodeName() throws Exception {
+        InstanceIdentifierContext<DataSchemaNode> iidContext = new InstanceIdentifierContext<>(iid, schemaNode, null, schema);
+        NormalizedNodeContext payload = new NormalizedNodeContext(iidContext, buildLeaf);
+        PutDataTransactionUtil.validTopLevelNodeName(iidContext.getInstanceIdentifier(), payload);
+
+        iidContext = new InstanceIdentifierContext<>(iid2, schemaNode2, null, schema);
+        payload = new NormalizedNodeContext(iidContext, buildBaseCont);
+        PutDataTransactionUtil.validTopLevelNodeName(iidContext.getInstanceIdentifier(), payload);
+    }
+
+    @Test(expected = RestconfDocumentedException.class)
+    public void testValidTopLevelNodeNamePathEmpty() throws Exception {
+        final InstanceIdentifierContext<DataSchemaNode> iidContext = new InstanceIdentifierContext<>(iid, schemaNode, null, schema);
+        final NormalizedNodeContext payload = new NormalizedNodeContext(iidContext, buildLeaf);
+        PutDataTransactionUtil.validTopLevelNodeName(YangInstanceIdentifier.EMPTY, payload);
+    }
+
+    @Test(expected = RestconfDocumentedException.class)
+    public void testValidTopLevelNodeNameWrongTopIdentifier() throws Exception {
+        final InstanceIdentifierContext<DataSchemaNode> iidContext = new InstanceIdentifierContext<>(iid, schemaNode, null, schema);
+        final NormalizedNodeContext payload = new NormalizedNodeContext(iidContext, buildLeaf);
+        PutDataTransactionUtil.validTopLevelNodeName(iid.getAncestor(1), payload);
+    }
+
+    @Test
+    public void testValidateListKeysEqualityInPayloadAndUri() throws Exception {
+        final InstanceIdentifierContext<DataSchemaNode> iidContext = new InstanceIdentifierContext<>(iid3, schemaNode3, null, schema);
+        final NormalizedNodeContext payload = new NormalizedNodeContext(iidContext, buildListEntry);
+        PutDataTransactionUtil.validateListKeysEqualityInPayloadAndUri(payload);
+    }
+
+    @Test
+    public void testPutContainerData() throws Exception {
+        final InstanceIdentifierContext<DataSchemaNode> iidContext = new InstanceIdentifierContext<>(iid2, schemaNode2, null, schema);
+        final NormalizedNodeContext payload = new NormalizedNodeContext(iidContext, buildBaseCont);
+
+        doReturn(readWrite).when(transactionChain).newReadWriteTransaction();
+        doReturn(read).when(transactionChain).newReadOnlyTransaction();
+        doReturn(write).when(transactionChain).newWriteOnlyTransaction();
+        doReturn(Futures.immediateCheckedFuture(Optional.absent())).when(read).read(LogicalDatastoreType.CONFIGURATION, iid2);
+        doNothing().when(write).put(LogicalDatastoreType.CONFIGURATION, payload.getInstanceIdentifierContext().getInstanceIdentifier(),
+                payload.getData());
+        doReturn(Futures.immediateCheckedFuture(null)).when(write).submit();
+
+        PutDataTransactionUtil.putData(payload, refSchemaCtx,
+                new TransactionVarsWrapper(payload.getInstanceIdentifierContext(), null, transactionChain));
+        verify(read).read(LogicalDatastoreType.CONFIGURATION, payload.getInstanceIdentifierContext().getInstanceIdentifier());
+        verify(write).put(LogicalDatastoreType.CONFIGURATION, payload.getInstanceIdentifierContext().getInstanceIdentifier(),
+                payload.getData());
+    }
+
+    @Test
+    public void testPutleafData() throws Exception {
+        final InstanceIdentifierContext<DataSchemaNode> iidContext = new InstanceIdentifierContext<>(iid, schemaNode, null, schema);
+        final NormalizedNodeContext payload = new NormalizedNodeContext(iidContext, buildLeaf);
+
+        doReturn(readWrite).when(transactionChain).newReadWriteTransaction();
+        doReturn(read).when(transactionChain).newReadOnlyTransaction();
+        doReturn(write).when(transactionChain).newWriteOnlyTransaction();
+        doReturn(Futures.immediateCheckedFuture(Optional.absent())).when(read).read(LogicalDatastoreType.CONFIGURATION, iid);
+        doNothing().when(write).put(LogicalDatastoreType.CONFIGURATION, payload.getInstanceIdentifierContext().getInstanceIdentifier(),
+                payload.getData());
+        doReturn(Futures.immediateCheckedFuture(null)).when(write).submit();
+
+        PutDataTransactionUtil.putData(payload, refSchemaCtx,
+                new TransactionVarsWrapper(payload.getInstanceIdentifierContext(), null, transactionChain));
+        verify(read).read(LogicalDatastoreType.CONFIGURATION, payload.getInstanceIdentifierContext().getInstanceIdentifier());
+        verify(write).put(LogicalDatastoreType.CONFIGURATION, payload.getInstanceIdentifierContext().getInstanceIdentifier(),
+                payload.getData());
+    }
+
+    @Test
+    public void testPutListData() throws Exception {
+        final InstanceIdentifierContext<DataSchemaNode> iidContext = new InstanceIdentifierContext<>(iid2, schemaNode2, null, schema);
+        final NormalizedNodeContext payload = new NormalizedNodeContext(iidContext, buildBaseContWithList);
+
+        doReturn(readWrite).when(transactionChain).newReadWriteTransaction();
+        doReturn(read).when(transactionChain).newReadOnlyTransaction();
+        doReturn(write).when(transactionChain).newWriteOnlyTransaction();
+        doReturn(Futures.immediateCheckedFuture(Optional.absent())).when(read).read(LogicalDatastoreType.CONFIGURATION, iid2);
+        doNothing().when(write).put(LogicalDatastoreType.CONFIGURATION, payload.getInstanceIdentifierContext().getInstanceIdentifier(),
+                payload.getData());
+        doReturn(Futures.immediateCheckedFuture(null)).when(write).submit();
+        PutDataTransactionUtil.putData(payload, refSchemaCtx,
+                new TransactionVarsWrapper(payload.getInstanceIdentifierContext(), null, transactionChain));
+        verify(read).read(LogicalDatastoreType.CONFIGURATION, iid2);
+        verify(write).put(LogicalDatastoreType.CONFIGURATION, iid2, payload.getData());
+    }
+
+}
+
diff --git a/restconf/sal-rest-connector/src/test/resources/jukebox/example-jukebox@2015-04-04.yang b/restconf/sal-rest-connector/src/test/resources/jukebox/example-jukebox@2015-04-04.yang
new file mode 100644 (file)
index 0000000..ddaa602
--- /dev/null
@@ -0,0 +1,243 @@
+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";
+          }
+        }
+      }
+   }
\ No newline at end of file