aboutsummaryrefslogtreecommitdiffstats
path: root/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreRequestHandlerSpec.groovy
blob: 641715d0d27fec744e33f8d8fb6c08e7ff049015 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
/*
 *  ============LICENSE_START=======================================================
 *  Copyright (C) 2023-2024 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.ncmp.rest.controller.handlers

import org.onap.cps.ncmp.api.NetworkCmProxyDataService
import org.onap.cps.ncmp.api.impl.exception.InvalidDatastoreException
import org.onap.cps.ncmp.api.impl.exception.InvalidOperationException
import org.onap.cps.ncmp.api.models.DataOperationDefinition
import org.onap.cps.ncmp.api.models.DataOperationRequest
import org.onap.cps.ncmp.api.models.CmResourceAddress
import org.onap.cps.ncmp.rest.exceptions.OperationNotSupportedException
import org.onap.cps.ncmp.rest.exceptions.PayloadTooLargeException
import org.onap.cps.ncmp.rest.executor.CpsNcmpTaskExecutor
import spock.lang.Specification
import spock.util.concurrent.PollingConditions

class NcmpDatastoreRequestHandlerSpec extends Specification {

    def spiedCpsNcmpTaskExecutor = Spy(CpsNcmpTaskExecutor)
    def mockNetworkCmProxyDataService = Mock(NetworkCmProxyDataService)

    def objectUnderTest = new NcmpPassthroughResourceRequestHandler(spiedCpsNcmpTaskExecutor, mockNetworkCmProxyDataService)

    def NO_AUTH_HEADER = null

    def setup() {
        objectUnderTest.timeOutInMilliSeconds = 100
    }

    def 'Attempt to execute async get request with #scenario.'() {
        given: 'notification feature is turned on/off'
            objectUnderTest.notificationFeatureEnabled = notificationFeatureEnabled
        and: 'a flag to track the network service call'
            def networkServiceMethodCalled = false
        and: 'a CM resource address'
            def cmResourceAddress = new CmResourceAddress('ds', 'ch1', 'resource1')
        and: 'the (mocked) service will use the flag to indicate if it is called'
            mockNetworkCmProxyDataService.getResourceDataForCmHandle(cmResourceAddress, 'options', _, _, NO_AUTH_HEADER) >>
                { networkServiceMethodCalled = true }
        when: 'get request is executed with topic = #topic'
            objectUnderTest.executeRequest(cmResourceAddress, 'options', topic, false, NO_AUTH_HEADER)
        then: 'the task is executed in an async fashion or not'
            expectedCalls * spiedCpsNcmpTaskExecutor.executeTask(*_)
        and: 'the service request is invoked'
            new PollingConditions().within(1) {
                assert networkServiceMethodCalled == true
            }
        where: 'the following parameters are used'
            scenario                   | notificationFeatureEnabled | topic   || expectedCalls
            'feature on, valid topic'  | true                       | 'valid' || 1
            'feature on, no topic'     | true                       | null    || 0
            'feature off, valid topic' | false                      | 'valid' || 0
            'feature off, no topic'    | false                      | null    || 0
    }

    def 'Attempt to execute async data operation request with feature #scenario.'() {
        given: 'a extended request handler that supports bulk requests'
           def objectUnderTest = new NcmpPassthroughResourceRequestHandler(spiedCpsNcmpTaskExecutor, mockNetworkCmProxyDataService)
        and: 'notification feature is turned on/off'
            objectUnderTest.notificationFeatureEnabled = notificationFeatureEnabled
        when: 'data operation request is executed'
            objectUnderTest.executeRequest('someTopic', new DataOperationRequest(), NO_AUTH_HEADER)
        then: 'the task is executed in an async fashion or not'
            expectedCalls * spiedCpsNcmpTaskExecutor.executeTaskWithErrorHandling(*_)
        where: 'the following parameters are used'
            scenario | notificationFeatureEnabled || expectedCalls
            'on'     | true                       || 1
            'off'    | false                      || 0
    }

    def 'Execute async data operation request with datastore #datastore.'() {
        given: 'notification feature is turned on'
            objectUnderTest.notificationFeatureEnabled = true
        and: 'a data operation request with datastore: #datastore'
            def dataOperationDefinition = new DataOperationDefinition(operation: 'read', datastore: datastore)
            def dataOperationRequest = new DataOperationRequest(dataOperationDefinitions: [dataOperationDefinition])
        and: ' a flag to track the network service call'
            def networkServiceMethodCalled = false
        and: 'the (mocked) service will use the flag to indicate it is called'
            mockNetworkCmProxyDataService.executeDataOperationForCmHandles('myTopic', dataOperationRequest, _, NO_AUTH_HEADER) >> {
                networkServiceMethodCalled = true
            }
        when: 'data operation request is executed'
            objectUnderTest.executeRequest('myTopic', dataOperationRequest, NO_AUTH_HEADER)
        then: 'the task is executed in an async fashion'
            1 * spiedCpsNcmpTaskExecutor.executeTaskWithErrorHandling(*_)
        and: 'the network service is invoked'
            new PollingConditions().within(1) {
                assert networkServiceMethodCalled == true
            }
        where: 'the following datastores are used'
            datastore << ['ncmp-datastore:passthrough-running', 'ncmp-datastore:passthrough-operational']
    }

    def 'Attempt to execute async data operation request with error #scenario'() {
        given: 'a data operation definition with datastore: #datastore'
            def dataOperationDefinition = new DataOperationDefinition(operation: 'read', datastore: datastore)
        when: 'data operation request is executed'
            def dataOperationRequest = new DataOperationRequest(dataOperationDefinitions: [dataOperationDefinition])
            objectUnderTest.executeRequest('myTopic', dataOperationRequest, NO_AUTH_HEADER)
        then: 'the correct error is thrown'
            def thrown = thrown(InvalidDatastoreException)
            assert thrown.message.contains(expectedErrorMessage)
        where: 'the following datastore names are used'
            scenario                | datastore                    || expectedErrorMessage
            'unsupported datastore' | 'ncmp-datastore:operational' || 'not supported'
            'invalid datastore'     | 'invalid'                    || 'invalid datastore name'
    }

    def 'Attempt to execute async data operation request with #scenario operation: #operation.'() {
        given: 'a data operation definition with operation: #operation'
            def dataOperationDefinition = new DataOperationDefinition(operation: operation, datastore: 'ncmp-datastore:passthrough-running')
        when: 'data operation request is executed'
            objectUnderTest.executeRequest('someTopic', new DataOperationRequest(dataOperationDefinitions:[dataOperationDefinition]), NO_AUTH_HEADER)
        then: 'the expected type of exception is thrown'
            thrown(expectedException)
        where: 'the following operations are used'
            scenario      | operation || expectedException
            'invalid'     | 'invalid' || InvalidOperationException
            'unsupported' | 'create'  || OperationNotSupportedException
            'unsupported' | 'update'  || OperationNotSupportedException
            'unsupported' | 'patch'   || OperationNotSupportedException
            'unsupported' | 'delete'  || OperationNotSupportedException
    }

    def 'Attempt to execute async data operation request with too many cm handles.'() {
        given: 'a data operation definition with too many cm handles'
            def tooMany = objectUnderTest.MAXIMUM_CM_HANDLES_PER_OPERATION+1
            def cmHandleIds = new String[tooMany]
            def dataOperationDefinition = new DataOperationDefinition(operationId: 'abc', operation: 'read', datastore: 'ncmp-datastore:passthrough-running', cmHandleIds: cmHandleIds)
        when: 'data operation request is executed'
            objectUnderTest.executeRequest('someTopic', new DataOperationRequest(dataOperationDefinitions:[dataOperationDefinition]), NO_AUTH_HEADER)
        then: 'a payload too large exception is thrown'
            def exceptionThrown = thrown(PayloadTooLargeException)
        and: 'the error message contains the offending number of cm handles'
            assert exceptionThrown.message == "Operation 'abc' affects too many (${tooMany}) cm handles"
    }

}