Merge "Expand CPS Service Integration Test (framework)"
authorToine Siebelink <toine.siebelink@est.tech>
Mon, 27 Feb 2023 17:21:26 +0000 (17:21 +0000)
committerGerrit Code Review <gerrit@onap.org>
Mon, 27 Feb 2023 17:21:26 +0000 (17:21 +0000)
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyCmHandlerQueryServiceImpl.java
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyCmHandlerQueryServiceSpec.groovy
cps-service/src/main/java/org/onap/cps/spi/FetchDescendantsOption.java
cps-service/src/test/groovy/org/onap/cps/api/impl/CpsQueryServiceImplSpec.groovy
cps-service/src/test/groovy/org/onap/cps/spi/FetchDescendantsOptionSpec.groovy
integration-test/src/test/groovy/org/onap/cps/integration/CpsPersistenceSpec.groovy [deleted file]
integration-test/src/test/groovy/org/onap/cps/integration/base/BookstoreSpecBase.groovy [new file with mode: 0644]
integration-test/src/test/groovy/org/onap/cps/integration/base/CpsIntegrationSpecBase.groovy [moved from integration-test/src/test/groovy/org/onap/cps/integration/CpsIntegrationSpecBase.groovy with 54% similarity]
integration-test/src/test/groovy/org/onap/cps/integration/base/TestConfig.groovy [moved from integration-test/src/test/groovy/org/onap/cps/integration/TestConfig.groovy with 99% similarity]
integration-test/src/test/groovy/org/onap/cps/integration/functional/CpsAdminServiceIntegrationSpec.groovy [new file with mode: 0644]
integration-test/src/test/groovy/org/onap/cps/integration/functional/CpsDataServiceIntegrationSpec.groovy [new file with mode: 0644]

index a98c600..f6f042d 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  ============LICENSE_START=======================================================
- *  Copyright (C) 2022 Nordix Foundation
+ *  Copyright (C) 2022-2023 Nordix Foundation
  *  Modifications Copyright (C) 2023 TechMahindra Ltd.
  *  ================================================================================
  *  Licensed under the Apache License, Version 2.0 (the "License");
@@ -24,7 +24,7 @@ package org.onap.cps.ncmp.api.impl;
 import static org.onap.cps.ncmp.api.impl.utils.RestQueryParametersValidator.validateCpsPathConditionProperties;
 import static org.onap.cps.ncmp.api.impl.utils.RestQueryParametersValidator.validateModuleNameConditionProperties;
 import static org.onap.cps.ncmp.api.impl.utils.YangDataConverter.convertYangModelCmHandleToNcmpServiceCmHandle;
-import static org.onap.cps.spi.FetchDescendantsOption.FETCH_DIRECT_CHILDREN_ONLY;
+import static org.onap.cps.spi.FetchDescendantsOption.DIRECT_CHILDREN_ONLY;
 import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS;
 
 import java.util.ArrayList;
@@ -304,7 +304,7 @@ public class NetworkCmProxyCmHandlerQueryServiceImpl implements NetworkCmProxyCm
     }
 
     private Set<String> getAllCmHandleIds() {
-        final DataNode dataNodes = inventoryPersistence.getDataNode("/dmi-registry", FETCH_DIRECT_CHILDREN_ONLY)
+        final DataNode dataNodes = inventoryPersistence.getDataNode("/dmi-registry", DIRECT_CHILDREN_ONLY)
                 .iterator().next();
         return dataNodes.getChildDataNodes().stream().map(dataNode -> dataNode.getLeaves().get("id").toString())
                 .collect(Collectors.toSet());
index 5cd702a..f2494e6 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  ============LICENSE_START=======================================================
- *  Copyright (C) 2022 Nordix Foundation
+ *  Copyright (C) 2022-2023 Nordix Foundation
  *  Modifications Copyright (C) 2023 TechMahindra Ltd.
  *  ================================================================================
  *  Licensed under the Apache License, Version 2.0 (the "License");
@@ -158,7 +158,7 @@ class NetworkCmProxyCmHandlerQueryServiceSpec extends Specification {
         given: 'We use an empty query'
             def cmHandleQueryParameters = new CmHandleQueryServiceParameters()
         and: 'the inventory persistence returns the dmi registry datanode with just ids'
-            mockInventoryPersistence.getDataNode("/dmi-registry", FetchDescendantsOption.FETCH_DIRECT_CHILDREN_ONLY) >> [dmiRegistry]
+            mockInventoryPersistence.getDataNode("/dmi-registry", FetchDescendantsOption.DIRECT_CHILDREN_ONLY) >> [dmiRegistry]
         and: 'the inventory persistence returns the dmi registry datanode with data'
             mockInventoryPersistence.getDataNode("/dmi-registry") >> [dmiRegistry]
         when: 'the query is executed for both cm handle ids and details'
index 0c8cddc..cf5e04d 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *  ============LICENSE_START=======================================================
  *  Copyright (C) 2021 Pantheon.tech
- *  Copyright (C) 2022 Nordix Foundation
+ *  Copyright (C) 2022-2023 Nordix Foundation
  *  Modifications Copyright (C) 2023 TechMahindra Ltd.
  *  ================================================================================
  *  Licensed under the Apache License, Version 2.0 (the "License");
@@ -30,15 +30,24 @@ import org.onap.cps.spi.exceptions.DataValidationException;
 @RequiredArgsConstructor
 public class FetchDescendantsOption {
 
-    public static final FetchDescendantsOption FETCH_DIRECT_CHILDREN_ONLY = new FetchDescendantsOption(1);
-    public static final FetchDescendantsOption OMIT_DESCENDANTS = new FetchDescendantsOption(0);
-    public static final FetchDescendantsOption INCLUDE_ALL_DESCENDANTS = new FetchDescendantsOption(-1);
+    public static final FetchDescendantsOption DIRECT_CHILDREN_ONLY
+        = new FetchDescendantsOption(1, "DirectChildrenOnly");
+    public static final FetchDescendantsOption OMIT_DESCENDANTS
+        = new FetchDescendantsOption(0, "OmitDescendants");
+    public static final FetchDescendantsOption INCLUDE_ALL_DESCENDANTS
+        = new FetchDescendantsOption(-1, "IncludeAllDescendants");
+
+    FetchDescendantsOption(final int depth) {
+        this(depth, "Depth=" + depth);
+    }
 
     private static final Pattern FETCH_DESCENDANTS_OPTION_PATTERN =
         Pattern.compile("^$|^all$|^none$|^[0-9]+$|^-1$");
 
     private final int depth;
 
+    private final String optionName;
+
     /**
      * Has next depth.
      *
@@ -85,6 +94,11 @@ public class FetchDescendantsOption {
         }
     }
 
+    @Override
+    public String toString() {
+        return optionName;
+    }
+
     private static void validateFetchDescendantsOption(final String fetchDescendantsOptionAsString) {
         if (Strings.isNullOrEmpty(fetchDescendantsOptionAsString)) {
             return;
index 60286b6..56c43d1 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  ============LICENSE_START=======================================================
- *  Copyright (C) 2021-2022 Nordix Foundation
+ *  Copyright (C) 2021-2023 Nordix Foundation
  *  Modifications Copyright (C) 2023 TechMahindra Ltd.
  *  ================================================================================
  *  Licensed under the Apache License, Version 2.0 (the "License");
@@ -45,7 +45,7 @@ class CpsQueryServiceImplSpec extends Specification {
             1 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName)
         where: 'all fetch descendants options are supported'
             fetchDescendantsOption << [FetchDescendantsOption.OMIT_DESCENDANTS, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS,
-                FetchDescendantsOption.FETCH_DIRECT_CHILDREN_ONLY, new FetchDescendantsOption(10)]
+                                       FetchDescendantsOption.DIRECT_CHILDREN_ONLY, new FetchDescendantsOption(10)]
     }
 
 }
index c4d3dd8..24f3487 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * ============LICENSE_START=======================================================
- *  Copyright (C) 2022 Nordix Foundation
+ *  Copyright (C) 2022-2023 Nordix Foundation
  *  Modifications Copyright (C) 2023 TechMahindra Ltd.
  *  ================================================================================
  *  Licensed under the Apache License, Version 2.0 (the "License");
 
 package org.onap.cps.spi
 
-import org.onap.cps.spi.exceptions.DataValidationException
 import spock.lang.Specification
 
 class FetchDescendantsOptionSpec extends Specification {
-    def 'Check has next descendant for fetch descendant option: #scenario'() {
+
+    def 'Has next descendant for fetch descendant option: #scenario'() {
         when: 'fetch descendant option with #depth depth'
             def fetchDescendantsOption = new FetchDescendantsOption(depth)
         then: 'next level descendants available: #expectedHasNext'
-            fetchDescendantsOption.hasNext() == expectedHasNext
+            assert fetchDescendantsOption.hasNext() == expectedHasNext
         where: 'following parameters are used'
             scenario                  | depth || expectedHasNext
             'omit descendants'        | 0     || false
@@ -38,7 +38,7 @@ class FetchDescendantsOptionSpec extends Specification {
             'include all descendants' | -1    || true
     }
 
-    def 'Check has next descendant for fetch descendant option: invalid depth'() {
+    def 'Has next descendant for fetch descendant option: invalid depth'() {
         given: 'fetch descendant option with -2 depth'
             def fetchDescendantsOption = new FetchDescendantsOption(-2)
         when: 'next level descendants not available'
@@ -47,7 +47,7 @@ class FetchDescendantsOptionSpec extends Specification {
             thrown IllegalArgumentException
     }
 
-    def 'Get next descendant for fetch descendant option: #scenario'() {
+    def 'Next descendant for fetch descendant option: #scenario.'() {
         when: 'fetch descendant option with #depth depth'
             def fetchDescendantsOption = new FetchDescendantsOption(depth)
         then: 'the next level of depth is as expected'
@@ -58,14 +58,14 @@ class FetchDescendantsOptionSpec extends Specification {
             'second child'            | 2
     }
 
-    def 'Get next descendant for fetch descendant option: include all descendants'() {
+    def 'Next descendant for fetch descendant option: include all descendants.'() {
         when: 'fetch descendant option with -1 depth'
             def fetchDescendantsOption = new FetchDescendantsOption(-1)
         then: 'the next level of depth is as expected'
             fetchDescendantsOption.next().depth == -1
     }
 
-    def 'Get next descendant for fetch descendant option: omit descendants'() {
+    def 'Next descendant for fetch descendant option: omit descendants.'() {
         given: 'fetch descendant option with 0 depth'
             def fetchDescendantsOption = new FetchDescendantsOption(0)
         when: 'the next level of depth is not allowed'
@@ -74,7 +74,7 @@ class FetchDescendantsOptionSpec extends Specification {
             thrown IllegalArgumentException
     }
 
-    def 'Create fetch descendant option with  descendant using #scenario'() {
+    def 'Create fetch descendant option with  descendant using #scenario.'() {
         when: 'the next level of depth is not allowed'
            def FetchDescendantsOption fetchDescendantsOption = FetchDescendantsOption.getFetchDescendantsOption(fetchDescendantsOptionAsString)
         then: 'fetch descendant object created'
@@ -87,4 +87,16 @@ class FetchDescendantsOptionSpec extends Specification {
             'No descendants using none'         | 'none'                         || 0
             'til 10th descendants using number' | '10'                           || 10
     }
+
+    def 'String values.'() {
+        expect: 'each fetch descendant option has the correct String value'
+            assert fetchDescendantsOption.toString() == expectedStringValue
+        where: 'the following option is used'
+            fetchDescendantsOption                         || expectedStringValue
+            FetchDescendantsOption.OMIT_DESCENDANTS        || 'OmitDescendants'
+            FetchDescendantsOption.DIRECT_CHILDREN_ONLY    || 'DirectChildrenOnly'
+            FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS || 'IncludeAllDescendants'
+            new FetchDescendantsOption(2)                  || 'Depth=2'
+    }
+
 }
diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/CpsPersistenceSpec.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/CpsPersistenceSpec.groovy
deleted file mode 100644 (file)
index 349f085..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- *  ============LICENSE_START=======================================================
- *  Copyright (C) 2023 Nordix Foundation
- *  Modifications Copyright (C) 2023 TechMahindra Ltd.
- *  ================================================================================
- *  Licensed under the Apache License, Version 2.0 (the 'License');
- *  you may not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an 'AS IS' BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- *
- *  SPDX-License-Identifier: Apache-2.0
- *  ============LICENSE_END=========================================================
- */
-
-package org.onap.cps.integration
-
-import org.onap.cps.spi.FetchDescendantsOption
-
-class CpsPersistenceSpec extends CpsIntegrationSpecBase{
-
-    def 'Test creation of test data'() {
-        when: 'A dataspace, schema set and anchor are persisted'
-            createDataspaceSchemaSetAnchor(TEST_DATASPACE, BOOKSTORE_SCHEMA_SET, 'bookstore.yang', TEST_ANCHOR)
-        and: 'data nodes are persisted under the created anchor'
-            saveDataNodes(TEST_DATASPACE, TEST_ANCHOR, '/', 'BookstoreDataNodes.json')
-        then: 'The dataspace has been persisted successfully'
-            cpsAdminService.getDataspace(TEST_DATASPACE).getName() == TEST_DATASPACE
-        and: 'The schema set has been persisted successfully'
-            cpsModuleService.getSchemaSet(TEST_DATASPACE, BOOKSTORE_SCHEMA_SET).getName() == BOOKSTORE_SCHEMA_SET
-        and: 'The anchor has been persisted successfully'
-            cpsAdminService.getAnchor(TEST_DATASPACE, TEST_ANCHOR).getName() == TEST_ANCHOR
-        and: 'The data nodes have been persisted successfully'
-            cpsDataService.getDataNodes(TEST_DATASPACE, TEST_ANCHOR, '/bookstore', FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS).iterator().next().xpath == '/bookstore'
-    }
-
-    def 'Test deletion of all test data'() {
-        when: 'delete all from test dataspace method is called'
-            deleteAllFromTestDataspace()
-        and: 'the test dataspace is deleted'
-            cpsAdminService.deleteDataspace(TEST_DATASPACE)
-        then: 'there is no test dataspace'
-            !cpsAdminService.getAllDataspaces().contains(TEST_DATASPACE)
-    }
-
-    def 'Read test for persisted data nodes'() {
-        given:'There is a test dataspace created'
-            cpsAdminService.createDataspace(TEST_DATASPACE)
-        and: 'There is a schema set and anchor for the test dataspace'
-            createSchemaSetAnchor(TEST_DATASPACE, 'bookstoreSchemaSet', 'bookstore.yang', TEST_ANCHOR)
-        when: 'data is persisted to the database'
-            saveDataNodes(TEST_DATASPACE, TEST_ANCHOR, "/", "BookstoreDataNodes.json")
-        then: 'the correct data is saved'
-            cpsDataService.getDataNodes(TEST_DATASPACE, TEST_ANCHOR, '/bookstore', FetchDescendantsOption.OMIT_DESCENDANTS).iterator().next().leaves['bookstore-name'] == 'Easons'
-    }
-}
diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/base/BookstoreSpecBase.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/base/BookstoreSpecBase.groovy
new file mode 100644 (file)
index 0000000..7eb47b3
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  Copyright (C) 2023 Nordix Foundation
+ *  ================================================================================
+ *  Licensed under the Apache License, Version 2.0 (the 'License');
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an 'AS IS' BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.integration.base
+
+import java.time.OffsetDateTime
+
+class BookstoreSpecBase extends CpsIntegrationSpecBase {
+
+    def static initialized = false
+
+    def setup() {
+        if (!initialized) {
+            setupBookstoreInfraStructure()
+            addBookstoreData()
+            initialized = true
+        }
+    }
+
+    def setupBookstoreInfraStructure() {
+        cpsAdminService.createDataspace(BOOKSTORE_DATASPACE)
+        def bookstoreYangModelAsString = readResourceFile('bookstore.yang')
+        cpsModuleService.createSchemaSet(BOOKSTORE_DATASPACE, BOOKSTORE_SCHEMA_SET, [bookstore : bookstoreYangModelAsString])
+        cpsAdminService.createAnchor(BOOKSTORE_DATASPACE, BOOKSTORE_SCHEMA_SET, BOOKSTORE_ANCHOR)
+    }
+
+    def addBookstoreData() {
+        def bookstoreJsonData = readResourceFile('BookstoreDataNodes.json')
+        cpsDataService.saveData(BOOKSTORE_DATASPACE, BOOKSTORE_ANCHOR, bookstoreJsonData, OffsetDateTime.now())
+    }
+
+}
  *  ============LICENSE_END=========================================================
  */
 
-package org.onap.cps.integration
+package org.onap.cps.integration.base
 
 import org.onap.cps.api.impl.CpsAdminServiceImpl
 import org.onap.cps.api.impl.CpsDataServiceImpl
 import org.onap.cps.api.impl.CpsModuleServiceImpl
-import org.onap.cps.spi.CascadeDeleteAllowed
+import org.onap.cps.integration.DatabaseTestContainer
+import org.onap.cps.spi.model.DataNode
 import org.onap.cps.spi.repository.DataspaceRepository
 import org.onap.cps.spi.impl.utils.CpsValidatorImpl
-import org.onap.cps.utils.ContentType
 import org.springframework.beans.factory.annotation.Autowired
 import org.springframework.boot.autoconfigure.EnableAutoConfiguration
 import org.springframework.boot.autoconfigure.domain.EntityScan
@@ -38,8 +38,6 @@ import org.testcontainers.spock.Testcontainers
 import spock.lang.Shared
 import spock.lang.Specification
 
-import java.time.OffsetDateTime
-
 @SpringBootTest(classes = [TestConfig, CpsAdminServiceImpl, CpsValidatorImpl])
 @Testcontainers
 @EnableAutoConfiguration
@@ -63,50 +61,35 @@ class CpsIntegrationSpecBase extends Specification {
     @Lazy
     CpsModuleServiceImpl cpsModuleService
 
-
-    def static TEST_DATASPACE = 'testDataspace'
+    def static GENERAL_TEST_DATASPACE = 'generalTestDataSpace'
+    def static BOOKSTORE_DATASPACE = 'bookstoreDataspace'
     def static BOOKSTORE_SCHEMA_SET = 'bookstoreSchemaSet'
-    def static TEST_ANCHOR = 'testAnchor'
+    def static BOOKSTORE_ANCHOR = 'bookstoreAnchor'
 
-    def createDataspaceSchemaSetAnchor(String dataspaceName, String schemaSetName, String schemaSetFileName, String anchorName) {
-        cpsAdminService.createDataspace(dataspaceName)
-        createSchemaSetAnchor(dataspaceName, schemaSetName, schemaSetFileName, anchorName)
-    }
+    def static initialized = false
 
-    def createSchemaSetAnchor(String dataspaceName, String schemaSetName, String schemaSetFileName, String anchorName) {
-        def bookstoreFileContent = readResourceFile(schemaSetFileName)
-        cpsModuleService.createSchemaSet(dataspaceName, schemaSetName, [(schemaSetFileName) : bookstoreFileContent])
-        cpsAdminService.createAnchor(dataspaceName, schemaSetName, anchorName)
+    def setup() {
+        if (!initialized) {
+            cpsAdminService.createDataspace(GENERAL_TEST_DATASPACE)
+            def bookstoreModelFileContent = readResourceFile('bookstore.yang')
+            cpsModuleService.createSchemaSet(GENERAL_TEST_DATASPACE, BOOKSTORE_SCHEMA_SET, [bookstore : bookstoreModelFileContent])
+            initialized = true;
+        }
     }
 
-    def saveDataNodes(String dataspaceName, String anchorName, String parentNodeXpath, String dataNodesFileName) {
-        def dataNodesAsJSON = readResourceFile(dataNodesFileName)
-        if (isRootXpath(parentNodeXpath)) {
-            cpsDataService.saveData(dataspaceName, anchorName, dataNodesAsJSON,
-                OffsetDateTime.now(), ContentType.JSON);
-        } else {
-            cpsDataService.saveData(dataspaceName, anchorName, parentNodeXpath,
-                dataNodesAsJSON, OffsetDateTime.now(), ContentType.JSON);
-        }
+    def static countDataNodesInTree(DataNode dataNode) {
+        return 1 + countDataNodesInTree(dataNode.getChildDataNodes())
     }
 
-    def deleteAllFromTestDataspace() {
-        def anchors = cpsAdminService.getAnchors(TEST_DATASPACE)
-        for(anchor in anchors) {
-            cpsDataService.deleteDataNodes(TEST_DATASPACE, anchor.getName(), OffsetDateTime.now())
-            cpsAdminService.deleteAnchor(TEST_DATASPACE, anchor.getName())
-        }
-        def schemaSets = cpsModuleService.getSchemaSets(TEST_DATASPACE)
-        for(schemaSet in schemaSets) {
-            cpsModuleService.deleteSchemaSet(TEST_DATASPACE, schemaSet.getName(), CascadeDeleteAllowed.CASCADE_DELETE_ALLOWED)
+    def static countDataNodesInTree(Collection<DataNode> dataNodes) {
+        int nodeCount = 0
+        for (DataNode parent : dataNodes) {
+            nodeCount += countDataNodesInTree(parent)
         }
+        return nodeCount
     }
 
-    def static readResourceFile(String filename) {
+    def static readResourceFile(filename) {
         return new File('src/test/resources/data/' + filename).text
     }
-
-    def static isRootXpath(final String xpath) {
-        return "/".equals(xpath);
-    }
 }
@@ -18,7 +18,7 @@
  *  ============LICENSE_END=========================================================
  */
 
-package org.onap.cps.integration
+package org.onap.cps.integration.base
 
 import com.fasterxml.jackson.databind.ObjectMapper
 import org.onap.cps.notification.NotificationService
diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/functional/CpsAdminServiceIntegrationSpec.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/functional/CpsAdminServiceIntegrationSpec.groovy
new file mode 100644 (file)
index 0000000..d504a9e
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  Copyright (C) 2023 Nordix Foundation
+ *  ================================================================================
+ *  Licensed under the Apache License, Version 2.0 (the 'License');
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an 'AS IS' BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.integration.functional
+
+import org.onap.cps.integration.base.CpsIntegrationSpecBase
+import org.onap.cps.spi.exceptions.AlreadyDefinedException
+import org.onap.cps.spi.exceptions.AnchorNotFoundException
+import org.onap.cps.spi.exceptions.DataspaceNotFoundException
+
+class CpsAdminServiceIntegrationSpec extends CpsIntegrationSpecBase {
+
+    def objectUnderTest
+
+    def setup() { objectUnderTest = cpsAdminService }
+
+    def 'Dataspace CRUD operations.'() {
+        when: 'a dataspace is created'
+            objectUnderTest.createDataspace('newDataspace')
+        then: 'the dataspace can be read'
+            assert objectUnderTest.getDataspace('newDataspace').name == 'newDataspace'
+        and: 'it can be deleted'
+            objectUnderTest.deleteDataspace('newDataspace')
+        then: 'the dataspace no longer exists i.e. an exception is thrown if an attempt is made to retrieve it'
+            def thrown = null
+            try {
+                objectUnderTest.getDataspace('newDataspace')
+            } catch(Exception e) {
+                thrown = e
+            }
+           assert thrown instanceof DataspaceNotFoundException
+    }
+
+    def 'Retrieve all dataspaces (depends on total test suite).'() {
+        given: 'two addtional dataspaces are created'
+            objectUnderTest.createDataspace('dataspace1')
+            objectUnderTest.createDataspace('dataspace2')
+        when: 'all datespaces are retreived'
+            def result = objectUnderTest.getAllDataspaces()
+        then: 'there are at least 3 dataspaces (2 new ones plus the general test dataspace)'
+            result.size() >= 3
+            assert result.name.containsAll([GENERAL_TEST_DATASPACE, 'dataspace1', 'dataspace2'])
+    }
+
+    def 'Duplicate dataspaces.'() {
+        when: 'attempting to create a dataspace with the same name as an existing one'
+            objectUnderTest.createDataspace(GENERAL_TEST_DATASPACE)
+        then: 'an exception is thrown indicating the dataspace already exists'
+            thrown(AlreadyDefinedException)
+    }
+
+    def 'Anchor CRUD operations.'() {
+        when: 'a anchor is created'
+            objectUnderTest.createAnchor(GENERAL_TEST_DATASPACE, BOOKSTORE_SCHEMA_SET, 'newAnchor')
+        then: 'the anchor be read'
+            assert objectUnderTest.getAnchor(GENERAL_TEST_DATASPACE, 'newAnchor').name == 'newAnchor'
+        and: 'it can be deleted'
+            objectUnderTest.deleteAnchor(GENERAL_TEST_DATASPACE,'newAnchor')
+        then: 'the anchor no longer exists i.e. an exception is thrown if an attempt is made to retrieve it'
+            def thrown = null
+            try {
+                objectUnderTest.getAnchor(GENERAL_TEST_DATASPACE, 'newAnchor')
+            } catch(Exception e) {
+                thrown = e
+            }
+            assert thrown instanceof AnchorNotFoundException
+    }
+
+    def 'Filtering multiple anchors.'() {
+        when: '2 anchors with bookstore schema set are created'
+            objectUnderTest.createAnchor(GENERAL_TEST_DATASPACE, BOOKSTORE_SCHEMA_SET, 'anchor1')
+            objectUnderTest.createAnchor(GENERAL_TEST_DATASPACE, BOOKSTORE_SCHEMA_SET, 'anchor2')
+        and: '1 anchor with "other" schema set is created'
+            def bookstoreModelFileContent = readResourceFile('bookstore.yang')
+            cpsModuleService.createSchemaSet(GENERAL_TEST_DATASPACE, 'otherSchemaSet', [someFileName: bookstoreModelFileContent])
+            objectUnderTest.createAnchor(GENERAL_TEST_DATASPACE, 'otherSchemaSet', 'anchor3')
+        then: 'there are 3 anchors in the general test database'
+            assert objectUnderTest.getAnchors(GENERAL_TEST_DATASPACE).size() == 3
+        and: 'there are 2 anchors associated with bookstore schema set'
+            assert objectUnderTest.getAnchors(GENERAL_TEST_DATASPACE, BOOKSTORE_SCHEMA_SET).size() == 2
+        and: 'there is 1 anchor associated with other schema set'
+            assert objectUnderTest.getAnchors(GENERAL_TEST_DATASPACE, 'otherSchemaSet').size() == 1
+    }
+
+    def 'Querying anchor(name)s (depends on previous test!).'() {
+        expect: 'there are now 3 anchors using the "stores" module (both schema sets use the same modules) '
+            assert objectUnderTest.queryAnchorNames(GENERAL_TEST_DATASPACE, ['stores']).size() == 3
+        and: 'there are no anchors using both "stores" and a "unused-model"'
+            assert objectUnderTest.queryAnchorNames(GENERAL_TEST_DATASPACE, ['stores', 'unused-model']).size() == 0
+    }
+
+}
diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/functional/CpsDataServiceIntegrationSpec.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/functional/CpsDataServiceIntegrationSpec.groovy
new file mode 100644 (file)
index 0000000..5e839f2
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  Copyright (C) 2023 Nordix Foundation
+ *  Modifications Copyright (C) 2023 TechMahindra Ltd.
+ *  ================================================================================
+ *  Licensed under the Apache License, Version 2.0 (the 'License');
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an 'AS IS' BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.integration.functional
+
+import org.onap.cps.integration.base.BookstoreSpecBase
+import org.onap.cps.spi.FetchDescendantsOption
+
+class CpsDataServiceIntegrationSpec extends BookstoreSpecBase {
+
+    def objectUnderTest
+
+    def setup() { objectUnderTest = cpsDataService }
+
+    def 'Read bookstore top-level container(s) using #fetchDescendantsOption.'() {
+        when: 'get data nodes for bookstore container'
+            def result = objectUnderTest.getDataNodes(BOOKSTORE_DATASPACE, BOOKSTORE_ANCHOR, '/bookstore', fetchDescendantsOption)
+        then: 'the tree consist ouf of #expectNumberOfDataNodes data nodes'
+            assert countDataNodesInTree(result) == expectNumberOfDataNodes
+        and: 'the top level data node has the expected attribute and value'
+            assert result.leaves['bookstore-name'] == ['Easons']
+        where: 'the following option is used'
+            fetchDescendantsOption                         || expectNumberOfDataNodes
+            FetchDescendantsOption.OMIT_DESCENDANTS        || 1
+            FetchDescendantsOption.DIRECT_CHILDREN_ONLY    || 4
+            FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS || 8
+            new FetchDescendantsOption(2)                  || 8
+    }
+
+}