diff options
Diffstat (limited to 'cps-ncmp-service')
2 files changed, 120 insertions, 70 deletions
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionPersistenceServiceImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionPersistenceServiceImpl.java index 92f3459187..0adf225fe4 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionPersistenceServiceImpl.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionPersistenceServiceImpl.java @@ -26,14 +26,12 @@ import java.io.Serializable; import java.time.OffsetDateTime; import java.util.Collection; import java.util.Collections; -import java.util.HashMap; import java.util.List; import java.util.Map; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.onap.cps.api.CpsDataService; import org.onap.cps.api.CpsQueryService; -import org.onap.cps.cpspath.parser.CpsPathUtil; import org.onap.cps.ncmp.api.impl.operations.DatastoreType; import org.onap.cps.spi.model.DataNode; import org.onap.cps.utils.ContentType; @@ -46,10 +44,13 @@ import org.springframework.stereotype.Service; public class CmNotificationSubscriptionPersistenceServiceImpl implements CmNotificationSubscriptionPersistenceService { private static final String SUBSCRIPTION_ANCHOR_NAME = "cm-data-subscriptions"; - private static final String CM_SUBSCRIPTION_CPS_PATH_QUERY = """ - /datastores/datastore[@name='%s']/cm-handles/cm-handle[@id='%s']/filters/filter[@xpath='%s'] + private static final String CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_AND_CMHANDLE = """ + /datastores/datastore[@name='%s']/cm-handles/cm-handle[@id='%s']/filters """.trim(); - private static final String SUBSCRIPTION_IDS_CPS_PATH_QUERY = """ + private static final String CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_CMHANDLE_AND_XPATH = + CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_AND_CMHANDLE + "/filter[@xpath='%s']"; + + private static final String CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_ID = """ //filter/subscriptionIds[text()='%s'] """.trim(); @@ -66,7 +67,7 @@ public class CmNotificationSubscriptionPersistenceServiceImpl implements CmNotif @Override public boolean isUniqueSubscriptionId(final String subscriptionId) { return cpsQueryService.queryDataNodes(NCMP_DATASPACE_NAME, SUBSCRIPTION_ANCHOR_NAME, - SUBSCRIPTION_IDS_CPS_PATH_QUERY.formatted(subscriptionId), + CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_ID.formatted(subscriptionId), OMIT_DESCENDANTS).isEmpty(); } @@ -75,8 +76,8 @@ public class CmNotificationSubscriptionPersistenceServiceImpl implements CmNotif final String cmHandleId, final String xpath) { final String isOngoingCmSubscriptionCpsPathQuery = - CM_SUBSCRIPTION_CPS_PATH_QUERY.formatted(datastoreType.getDatastoreName(), cmHandleId, - escapeQuotesByDoublingThem(xpath)); + CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_CMHANDLE_AND_XPATH.formatted( + datastoreType.getDatastoreName(), cmHandleId, escapeQuotesByDoublingThem(xpath)); final Collection<DataNode> existingNodes = cpsQueryService.queryDataNodes(NCMP_DATASPACE_NAME, CM_SUBSCRIPTIONS_ANCHOR_NAME, isOngoingCmSubscriptionCpsPathQuery, OMIT_DESCENDANTS); @@ -89,75 +90,85 @@ public class CmNotificationSubscriptionPersistenceServiceImpl implements CmNotif @Override public void addCmNotificationSubscription(final DatastoreType datastoreType, final String cmHandleId, final String xpath, final String subscriptionId) { - if (isOngoingCmNotificationSubscription(datastoreType, cmHandleId, xpath) - && (!getOngoingCmNotificationSubscriptionIds(datastoreType, cmHandleId, xpath) - .contains(subscriptionId))) { - final DataNode subscriptionAsDataNode = getSubscriptionAsDataNode(datastoreType, cmHandleId, xpath); - final Collection<String> subscriptionIds = getOngoingCmNotificationSubscriptionIds(datastoreType, - cmHandleId, xpath); + final Collection<String> subscriptionIds = getOngoingCmNotificationSubscriptionIds(datastoreType, + cmHandleId, xpath); + if (subscriptionIds.isEmpty()) { + addFirstSubscriptionForDatastoreCmHandleAndXpath(datastoreType, cmHandleId, xpath, subscriptionId); + } else if (!subscriptionIds.contains(subscriptionId)) { subscriptionIds.add(subscriptionId); - saveSubscriptionDetails(subscriptionAsDataNode, subscriptionIds); - } else { - addNewSubscriptionViaDatastore(datastoreType, cmHandleId, xpath, subscriptionId); + saveSubscriptionDetails(datastoreType, cmHandleId, xpath, subscriptionIds); } } @Override public void removeCmNotificationSubscription(final DatastoreType datastoreType, final String cmHandleId, final String xpath, final String subscriptionId) { - final DataNode subscriptionAsDataNode = getSubscriptionAsDataNode(datastoreType, cmHandleId, xpath); final Collection<String> subscriptionIds = getOngoingCmNotificationSubscriptionIds(datastoreType, cmHandleId, xpath); - subscriptionIds.remove(subscriptionId); - saveSubscriptionDetails(subscriptionAsDataNode, subscriptionIds); - if (isOngoingCmNotificationSubscription(datastoreType, cmHandleId, xpath)) { - log.info("There are subscribers left for the following cps path {} :", - CM_SUBSCRIPTION_CPS_PATH_QUERY.formatted(datastoreType.getDatastoreName(), cmHandleId, - escapeQuotesByDoublingThem(xpath))); - } else { - log.info("No subscribers left for the following cps path {} :", - CM_SUBSCRIPTION_CPS_PATH_QUERY.formatted(datastoreType.getDatastoreName(), cmHandleId, - escapeQuotesByDoublingThem(xpath))); - deleteListOfSubscriptionsFor(datastoreType, cmHandleId, xpath); + if (subscriptionIds.remove(subscriptionId)) { + if (isOngoingCmNotificationSubscription(datastoreType, cmHandleId, xpath)) { + saveSubscriptionDetails(datastoreType, cmHandleId, xpath, subscriptionIds); + log.info("There are subscribers left for the following cps path {} :", + CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_CMHANDLE_AND_XPATH.formatted( + datastoreType.getDatastoreName(), cmHandleId, escapeQuotesByDoublingThem(xpath))); + } else { + log.info("No subscribers left for the following cps path {} :", + CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_CMHANDLE_AND_XPATH.formatted( + datastoreType.getDatastoreName(), cmHandleId, escapeQuotesByDoublingThem(xpath))); + deleteListOfSubscriptionsFor(datastoreType, cmHandleId, xpath); + } } } private void deleteListOfSubscriptionsFor(final DatastoreType datastoreType, final String cmHandleId, final String xpath) { cpsDataService.deleteDataNode(NCMP_DATASPACE_NAME, SUBSCRIPTION_ANCHOR_NAME, - CM_SUBSCRIPTION_CPS_PATH_QUERY.formatted(datastoreType.getDatastoreName(), cmHandleId, - escapeQuotesByDoublingThem(xpath)), + CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_CMHANDLE_AND_XPATH.formatted( + datastoreType.getDatastoreName(), cmHandleId, escapeQuotesByDoublingThem(xpath)), OffsetDateTime.now()); } - private DataNode getSubscriptionAsDataNode(final DatastoreType datastoreType, final String cmHandleId, - final String xpath) { - return cpsQueryService.queryDataNodes(NCMP_DATASPACE_NAME, SUBSCRIPTION_ANCHOR_NAME, - CM_SUBSCRIPTION_CPS_PATH_QUERY.formatted(datastoreType.getDatastoreName(), cmHandleId, - escapeQuotesByDoublingThem(xpath)), - OMIT_DESCENDANTS).iterator().next(); + private boolean isFirstSubscriptionForCmHandle(final DatastoreType datastoreType, final String cmHandleId) { + return cpsQueryService.queryDataNodes(NCMP_DATASPACE_NAME, CM_SUBSCRIPTIONS_ANCHOR_NAME, + CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_AND_CMHANDLE.formatted( + datastoreType.getDatastoreName(), cmHandleId), + OMIT_DESCENDANTS).isEmpty(); } - private void addNewSubscriptionViaDatastore(final DatastoreType datastoreType, final String cmHandleId, - final String xpath, final String newSubscriptionId) { - final String parentXpath = "/datastores/datastore[@name='%s']/cm-handles" - .formatted(datastoreType.getDatastoreName()); - final String subscriptionAsJson = String.format("{\"cm-handle\":[{\"id\":\"%s\",\"filters\":{\"filter\":" - + "[{\"xpath\":\"%s\",\"subscriptionIds\":[\"%s\"]}]}}]}", cmHandleId, xpath, newSubscriptionId); - cpsDataService.saveData(NCMP_DATASPACE_NAME, SUBSCRIPTION_ANCHOR_NAME, parentXpath, subscriptionAsJson, - OffsetDateTime.now(), ContentType.JSON); + private void addFirstSubscriptionForDatastoreCmHandleAndXpath(final DatastoreType datastoreType, + final String cmHandleId, + final String xpath, + final String subscriptionId) { + final Collection<String> newSubscriptionList = Collections.singletonList(subscriptionId); + final String subscriptionDetailsAsJson = getSubscriptionDetailsAsJson(xpath, newSubscriptionList); + if (isFirstSubscriptionForCmHandle(datastoreType, cmHandleId)) { + final String parentXpath = "/datastores/datastore[@name='%s']/cm-handles" + .formatted(datastoreType.getDatastoreName()); + final String subscriptionAsJson = String.format("{\"cm-handle\":[{\"id\":\"%s\",\"filters\":%s}]}", + cmHandleId, subscriptionDetailsAsJson); + cpsDataService.saveData(NCMP_DATASPACE_NAME, SUBSCRIPTION_ANCHOR_NAME, parentXpath, subscriptionAsJson, + OffsetDateTime.now(), ContentType.JSON); + } else { + cpsDataService.saveListElements(NCMP_DATASPACE_NAME, CM_SUBSCRIPTIONS_ANCHOR_NAME, + CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_AND_CMHANDLE.formatted( + datastoreType.getDatastoreName(), cmHandleId), + subscriptionDetailsAsJson, OffsetDateTime.now()); + } } - private void saveSubscriptionDetails(final DataNode subscriptionDetailsAsDataNode, + private void saveSubscriptionDetails(final DatastoreType datastoreType, final String cmHandleId, + final String xpath, final Collection<String> subscriptionIds) { - final Map<String, Serializable> subscriptionDetailsAsMap = new HashMap<>(); - subscriptionDetailsAsMap.put("xpath", subscriptionDetailsAsDataNode.getLeaves().get("xpath")); - subscriptionDetailsAsMap.put("subscriptionIds", (Serializable) subscriptionIds); - final String parentXpath = CpsPathUtil.getNormalizedParentXpath(subscriptionDetailsAsDataNode.getXpath()); - final String subscriptionDetailsAsJson = "{\"filter\":[" - + jsonObjectMapper.asJsonString(subscriptionDetailsAsMap).replace("'", "\"") + "]}"; - cpsDataService.updateNodeLeaves(NCMP_DATASPACE_NAME, SUBSCRIPTION_ANCHOR_NAME, parentXpath, - subscriptionDetailsAsJson, OffsetDateTime.now()); + final String subscriptionDetailsAsJson = getSubscriptionDetailsAsJson(xpath, subscriptionIds); + cpsDataService.updateNodeLeaves(NCMP_DATASPACE_NAME, CM_SUBSCRIPTIONS_ANCHOR_NAME, + CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_AND_CMHANDLE.formatted( + datastoreType.getDatastoreName(), cmHandleId), subscriptionDetailsAsJson, OffsetDateTime.now()); + } + + private String getSubscriptionDetailsAsJson(final String xpath, final Collection<String> subscriptionIds) { + final Map<String, Serializable> subscriptionDetailsAsMap = + Map.of("xpath", xpath, "subscriptionIds", (Serializable) subscriptionIds); + return "{\"filter\":[" + jsonObjectMapper.asJsonString(subscriptionDetailsAsMap) + "]}"; } private static String escapeQuotesByDoublingThem(final String inputXpath) { diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionPersistenceServiceImplSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionPersistenceServiceImplSpec.groovy index 13a20a1eb2..b51ecb0cf1 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionPersistenceServiceImplSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionPersistenceServiceImplSpec.groovy @@ -20,6 +20,12 @@ package org.onap.cps.ncmp.api.impl.events.cmsubscription.service +import org.onap.cps.utils.ContentType + +import static org.onap.cps.ncmp.api.impl.events.cmsubscription.service.CmNotificationSubscriptionPersistenceServiceImpl.CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_ID; +import static org.onap.cps.ncmp.api.impl.events.cmsubscription.service.CmNotificationSubscriptionPersistenceServiceImpl.CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_AND_CMHANDLE; +import static org.onap.cps.ncmp.api.impl.events.cmsubscription.service.CmNotificationSubscriptionPersistenceServiceImpl.CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_CMHANDLE_AND_XPATH; + import org.onap.cps.api.CpsDataService import org.onap.cps.api.CpsQueryService import org.onap.cps.ncmp.api.impl.operations.DatastoreType @@ -55,7 +61,7 @@ class CmNotificationSubscriptionPersistenceServiceImplSpec extends Specification def 'Checking uniqueness of incoming subscription ID'() { given: 'a cps path with a subscription ID for querying' - def cpsPathQuery = objectUnderTest.SUBSCRIPTION_IDS_CPS_PATH_QUERY.formatted('some-sub') + def cpsPathQuery = CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_ID.formatted('some-sub') and: 'relevant datanodes are returned' 1 * mockCpsQueryService.queryDataNodes('NCMP-Admin', 'cm-data-subscriptions', cpsPathQuery, FetchDescendantsOption.OMIT_DESCENDANTS) >> dataNodes @@ -71,7 +77,7 @@ class CmNotificationSubscriptionPersistenceServiceImplSpec extends Specification def 'Add new subscriber to an ongoing cm notification subscription'() { given: 'a valid cm subscription path query' - def cpsPathQuery = objectUnderTest.CM_SUBSCRIPTION_CPS_PATH_QUERY.formatted('ncmp-datastore:passthrough-running', 'ch-1', '/x/y') + def cpsPathQuery =CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_CMHANDLE_AND_XPATH.formatted('ncmp-datastore:passthrough-running', 'ch-1', '/x/y') and: 'a dataNode exists for the given cps path query' mockCpsQueryService.queryDataNodes('NCMP-Admin', 'cm-data-subscriptions', cpsPathQuery, FetchDescendantsOption.OMIT_DESCENDANTS) >> [new DataNode(xpath: cpsPathQuery, leaves: ['xpath': '/x/y','subscriptionIds': ['sub-1']])] @@ -82,26 +88,59 @@ class CmNotificationSubscriptionPersistenceServiceImplSpec extends Specification 'NCMP-Admin', 'cm-data-subscriptions', '/datastores/datastore[@name=\'ncmp-datastore:passthrough-running\']/cm-handles/cm-handle[@id=\'ch-1\']/filters', - '{"filter":[{"xpath":"/x/y","subscriptionIds":["sub-1","newSubId"]}]}', _) + objectUnderTest.getSubscriptionDetailsAsJson('/x/y', ['sub-1','newSubId']), _) } def 'Add new cm notification subscription for #datastoreType'() { given: 'a valid cm subscription path query' - def cpsPathQuery = objectUnderTest.CM_SUBSCRIPTION_CPS_PATH_QUERY.formatted(datastoreName, 'ch-1', '/x/y') - and: 'a parent node xpath for given path above' + def cmSubscriptionCpsPathQuery = CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_CMHANDLE_AND_XPATH.formatted(datastoreName, 'ch-1', '/x/y') + def cmHandleForSubscriptionPathQuery = CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_AND_CMHANDLE.formatted(datastoreName, 'ch-1') + and: 'a parent node xpath for the cm subscription path above' def parentNodeXpath = '/datastores/datastore[@name=\'%s\']/cm-handles' - and: 'a datanode does not exist for the given cps path query' + and: 'a datanode does not exist for cm subscription path query' mockCpsQueryService.queryDataNodes('NCMP-Admin', 'cm-data-subscriptions', - cpsPathQuery.formatted(datastoreName), + cmSubscriptionCpsPathQuery, FetchDescendantsOption.OMIT_DESCENDANTS) >> [] + and: 'a datanode does not exist for the given cm handle subscription path query' + mockCpsQueryService.queryDataNodes('NCMP-Admin', 'cm-data-subscriptions', + cmHandleForSubscriptionPathQuery, FetchDescendantsOption.OMIT_DESCENDANTS) >> [] + and: 'subscription is mapped as JSON' + def subscriptionAsJson = '{"cm-handle":[{"id":"ch-1","filters":' + + objectUnderTest.getSubscriptionDetailsAsJson('/x/y', ['newSubId']) + '}]}' when: 'the method to add/update cm notification subscription is called' objectUnderTest.addCmNotificationSubscription(datastoreType, 'ch-1','/x/y', 'newSubId') - then: 'data service method to update list of subscribers is called once with the correct parameters' + then: 'data service method to create new subscription for given subscriber is called once with the correct parameters' 1 * mockCpsDataService.saveData( 'NCMP-Admin', 'cm-data-subscriptions', parentNodeXpath.formatted(datastoreName), - '{"cm-handle":[{"id":"ch-1","filters":{"filter":[{"xpath":"/x/y","subscriptionIds":["newSubId"]}]}}]}', _,_) + subscriptionAsJson,_, ContentType.JSON) + where: + scenario | datastoreType || datastoreName + 'passthrough_running' | DatastoreType.PASSTHROUGH_RUNNING || "ncmp-datastore:passthrough-running" + 'passthrough_operational' | DatastoreType.PASSTHROUGH_OPERATIONAL || "ncmp-datastore:passthrough-operational" + } + + def 'Add new cm notification subscription when xpath does not exist for existing subscription cm handle'() { + given: 'a valid cm subscription path query' + def cmSubscriptionCpsPathQuery = CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_CMHANDLE_AND_XPATH.formatted(datastoreName, 'ch-1', '/x/y') + def cmHandleForSubscriptionPathQuery = CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_AND_CMHANDLE.formatted(datastoreName, 'ch-1') + and: 'a parent node xpath for given cm handle for subscription path above' + def parentNodeXpath = '/datastores/datastore[@name=\'%s\']/cm-handles/cm-handle[@id=\'%s\']/filters' + and: 'a datanode does not exist for cm subscription path query' + mockCpsQueryService.queryDataNodes('NCMP-Admin', 'cm-data-subscriptions', + cmSubscriptionCpsPathQuery, FetchDescendantsOption.OMIT_DESCENDANTS) >> [] + and: 'a datanode exists for the given cm handle subscription path query' + mockCpsQueryService.queryDataNodes('NCMP-Admin', 'cm-data-subscriptions', + cmHandleForSubscriptionPathQuery, FetchDescendantsOption.OMIT_DESCENDANTS) >> [new DataNode()] + when: 'the method to add/update cm notification subscription is called' + objectUnderTest.addCmNotificationSubscription(datastoreType, 'ch-1','/x/y', 'newSubId') + then: 'data service method to create new subscription for given subscriber is called once with the correct parameters' + 1 * mockCpsDataService.saveListElements( + 'NCMP-Admin', + 'cm-data-subscriptions', + parentNodeXpath.formatted(datastoreName, 'ch-1'), + objectUnderTest.getSubscriptionDetailsAsJson('/x/y', ['newSubId']),_) where: scenario | datastoreType || datastoreName 'passthrough_running' | DatastoreType.PASSTHROUGH_RUNNING || "ncmp-datastore:passthrough-running" @@ -110,7 +149,7 @@ class CmNotificationSubscriptionPersistenceServiceImplSpec extends Specification def 'Remove subscriber from a list of an ongoing cm notification subscription'() { given: 'a subscription exists when queried' - def cpsPathQuery = objectUnderTest.CM_SUBSCRIPTION_CPS_PATH_QUERY.formatted('ncmp-datastore:passthrough-running', 'ch-1', '/x/y') + def cpsPathQuery = CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_CMHANDLE_AND_XPATH.formatted('ncmp-datastore:passthrough-running', 'ch-1', '/x/y') mockCpsQueryService.queryDataNodes('NCMP-Admin', 'cm-data-subscriptions', cpsPathQuery, FetchDescendantsOption.OMIT_DESCENDANTS) >> [new DataNode(xpath: cpsPathQuery, leaves: ['xpath': '/x/y','subscriptionIds': ['sub-1', 'sub-2']])] when: 'the subscriber is removed' @@ -118,15 +157,15 @@ class CmNotificationSubscriptionPersistenceServiceImplSpec extends Specification then: 'the list of subscribers is updated' 1 * mockCpsDataService.updateNodeLeaves('NCMP-Admin', 'cm-data-subscriptions', '/datastores/datastore[@name=\'ncmp-datastore:passthrough-running\']/cm-handles/cm-handle[@id=\'ch-1\']/filters', - '{"filter":[{"xpath":"/x/y","subscriptionIds":["sub-2"]}]}', _) + objectUnderTest.getSubscriptionDetailsAsJson('/x/y', ['sub-2']), _) } - def 'Removing ongoing subscription with no subscribers'(){ - given: 'a subscription exists when queried but has no subscribers' - def cpsPathQuery = objectUnderTest.CM_SUBSCRIPTION_CPS_PATH_QUERY.formatted('ncmp-datastore:passthrough-running', 'ch-1', '/x/y') + def 'Removing last ongoing subscription for datastore, cmhandle and xpath'(){ + given: 'a subscription exists when queried but has only 1 subscriber' + def cpsPathQuery = CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_CMHANDLE_AND_XPATH.formatted('ncmp-datastore:passthrough-running', 'ch-1', '/x/y') mockCpsQueryService.queryDataNodes('NCMP-Admin', 'cm-data-subscriptions', - cpsPathQuery, FetchDescendantsOption.OMIT_DESCENDANTS) >> [new DataNode(xpath: cpsPathQuery, leaves: ['xpath': '/x/y','subscriptionIds': []])] - when: 'a an ongoing subscription is refreshed' + cpsPathQuery, FetchDescendantsOption.OMIT_DESCENDANTS) >> [new DataNode(xpath: cpsPathQuery, leaves: ['xpath': '/x/y','subscriptionIds': ['sub-1']])] + when: 'that last ongoing subscription is removed' objectUnderTest.removeCmNotificationSubscription(DatastoreType.PASSTHROUGH_RUNNING, 'ch-1', '/x/y', 'sub-1') then: 'the subscription with empty subscriber list is removed' 1 * mockCpsDataService.deleteDataNode('NCMP-Admin', 'cm-data-subscriptions', |