Support operation field for CPS Temporal Query Output API
[cps/cps-temporal.git] / src / test / groovy / org / onap / cps / temporal / repository / NetworkDataRepositoryImplSpec.groovy
1 /*
2  * ============LICENSE_START=======================================================
3  * Copyright (c) 2021-2022 Bell Canada.
4  * ================================================================================
5  * Licensed under the Apache License, Version 2.0 (the "License");
6   * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *        http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  * SPDX-License-Identifier: Apache-2.0
18  * ============LICENSE_END=========================================================
19  */
20
21 package org.onap.cps.temporal.repository
22
23 import org.onap.cps.temporal.domain.NetworkData
24 import org.onap.cps.temporal.domain.Operation
25 import org.onap.cps.temporal.domain.SearchCriteria
26 import org.onap.cps.temporal.repository.containers.TimescaleContainer
27 import org.springframework.beans.factory.annotation.Autowired
28 import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase
29 import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest
30 import org.springframework.data.domain.PageRequest
31 import org.springframework.data.domain.Slice
32 import org.springframework.data.domain.Sort
33 import org.springframework.test.context.jdbc.Sql
34 import org.testcontainers.spock.Testcontainers
35 import org.springframework.test.annotation.Rollback
36 import spock.lang.Shared
37 import spock.lang.Specification
38 import java.time.LocalDateTime
39 import java.time.OffsetDateTime
40 import java.time.ZoneOffset
41 import java.time.format.DateTimeFormatter
42
43 /**
44  * Test specification for network data repository.
45  */
46 @Testcontainers
47 @DataJpaTest
48 @Rollback(false)
49 @AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
50 class NetworkDataRepositoryImplSpec extends Specification {
51
52     static final String RELOAD_DATA_FOR_SEARCHING = '/data/network-data-changes.sql'
53
54     @Shared
55     DateTimeFormatter ISO_TIMESTAMP_FORMATTER = DateTimeFormatter.ofPattern('yyyy-MM-dd HH:mm:ss.SSS')
56
57     def queryDataspaceName = 'DATASPACE-01'
58     @Shared
59     def querySchemaSetName = 'SCHEMA-SET-01'
60     @Shared
61     def queryAnchorName = 'ANCHOR-01'
62
63     @Shared
64     def observedAscSortOrder = new Sort.Order(Sort.Direction.ASC, 'observed_timestamp')
65     @Shared
66     def observedDescSortOrder = new Sort.Order(Sort.Direction.DESC, 'observed_timestamp')
67     @Shared
68     def anchorAscSortOrder = new Sort.Order(Sort.Direction.ASC, 'anchor')
69
70     @Autowired
71     NetworkDataRepository networkDataRepository
72
73     @Shared
74     TimescaleContainer databaseTestContainer = TimescaleContainer.getInstance()
75
76     @Sql([RELOAD_DATA_FOR_SEARCHING])
77     def 'Query: pagination for #scenario'() {
78         given: 'search criteria'
79             def searchCriteria = (new SearchCriteria.Builder())
80                 .dataspaceName(queryDataspaceName)
81                 .anchorName(queryAnchorName)
82                 .pagination(pageNumber, 1)
83                 .build()
84         when: 'data is fetched'
85             Slice<NetworkData> result = networkDataRepository.findBySearchCriteria(searchCriteria)
86         then: 'result has expected values'
87             result.getNumberOfElements() == 1L
88             NetworkData networkData = result.getContent().get(0);
89             networkData.getAnchor() == queryAnchorName
90             networkData.getDataspace() == queryDataspaceName
91             networkData.getSchemaSet() == querySchemaSetName
92         and: ' correct pagination details'
93             result.hasNext() ? result.nextPageable() : null == expectedNextPageable
94             result.hasPrevious() ? result.previousPageable() : null == expectedPreviousPageable
95         where:
96             scenario      | pageNumber || expectedPreviousPageable | expectedNextPageable
97             'first Page'  | 0          || null                     | PageRequest.of(1, 1)
98             'middle Page' | 1          || PageRequest.of(0, 1)     | PageRequest.of(2, 1)
99             'last Page'   | 2          || PageRequest.of(1, 1)     | null
100     }
101
102     @Sql([RELOAD_DATA_FOR_SEARCHING])
103     def 'Query: filter by observed after.'() {
104         given: 'observed after date'
105             def observedAfter = getOffsetDateDate('2021-07-22 01:00:01.000')
106         and: 'search criteria'
107             def searchCriteria = (new SearchCriteria.Builder())
108                 .dataspaceName(queryDataspaceName)
109                 .anchorName(queryAnchorName)
110                 .observedAfter(observedAfter)
111                 .pagination(0, 4)
112                 .build()
113         when: 'data is fetched'
114             Slice<NetworkData> result = networkDataRepository.findBySearchCriteria(searchCriteria)
115         then: 'result have expected number of record'
116             result.getNumberOfElements() == 2L
117         and: 'each record has observed timestamp on or after the provided value'
118             for (NetworkData data : result.getContent()) {
119                 assert data.getObservedTimestamp().isAfter(observedAfter) || data.getObservedTimestamp().isEqual(observedAfter)
120                 assert data.getAnchor() == queryAnchorName
121                 assert data.getDataspace() == queryDataspaceName
122                 assert data.getOperation() != null
123             }
124     }
125
126     @Sql([RELOAD_DATA_FOR_SEARCHING])
127     def 'Query: filter by created before.'() {
128         given: 'created before date'
129             def createdBefore = getOffsetDateDate('2021-07-22 23:00:01.000')
130         and: 'search criteria'
131             def searchCriteria = (new SearchCriteria.Builder())
132                 .dataspaceName(queryDataspaceName)
133                 .anchorName(queryAnchorName)
134                 .createdBefore(createdBefore)
135                 .pagination(0, 4)
136                 .build()
137         when: 'data is fetched'
138             Slice<NetworkData> result = networkDataRepository.findBySearchCriteria(searchCriteria)
139         then: 'result have expected number of record'
140             result.getNumberOfElements() == 2L
141         and: 'each record has observed timestamp on or after the provided value'
142             for (NetworkData data : result.getContent()) {
143                 assert data.getCreatedTimestamp().isBefore(createdBefore) || data.getCreatedTimestamp().isEqual(createdBefore)
144                 assert data.getAnchor() == queryAnchorName
145                 assert data.getDataspace() == queryDataspaceName
146                 assert data.getOperation() != null
147             }
148     }
149
150     @Sql([RELOAD_DATA_FOR_SEARCHING])
151     def 'Query: sort data by #scenario.'() {
152         given: 'search criteria'
153             def searchCriteria = (new SearchCriteria.Builder())
154                 .dataspaceName(queryDataspaceName)
155                 .schemaSetName(querySchemaSetName)
156                 .sort(sortOrder)
157                 .pagination(0, 4)
158                 .build()
159         when: 'data is fetched'
160             Slice<NetworkData> result = networkDataRepository.findBySearchCriteria(searchCriteria)
161         then: 'result has expected values'
162             result.getNumberOfElements() == 4L
163             with(result.getContent().get(0)) {
164                 dataspace == queryDataspaceName
165                 schemaSet == querySchemaSetName
166                 anchor == expectedAnchorName
167                 observedTimestamp == getOffsetDateDate(expectedObservedTimestamp)
168             }
169         where:
170             scenario                      | sortOrder                                          || expectedObservedTimestamp | expectedAnchorName
171             'observed timestamp desc'     | Sort.by(observedDescSortOrder)                     || '2021-07-24 00:00:01.000' | 'ANCHOR-02'
172             'anchor asc, ' +
173                 'observed timestamp desc' | Sort.by(anchorAscSortOrder, observedDescSortOrder) || '2021-07-23 00:00:01.000' | 'ANCHOR-01'
174
175     }
176
177     @Sql([RELOAD_DATA_FOR_SEARCHING])
178     def 'Query: filter by payload.'() {
179         def dataspaceName = 'DATASPACE-02'
180         given: 'search criteria'
181             def searchCriteria = (new SearchCriteria.Builder())
182                 .dataspaceName(dataspaceName)
183                 .schemaSetName(querySchemaSetName)
184                 .simplePayloadFilter(simplePayloadFilter)
185                 .pagination(0, 4)
186                 .build()
187         when: 'data is fetched'
188             Slice<NetworkData> result = networkDataRepository.findBySearchCriteria(searchCriteria)
189         then: 'result has expected values'
190             result.getNumberOfElements() == expectedRecordsCount
191             with(result.getContent().get(0)) {
192                 dataspace == dataspaceName
193                 schemaSet == querySchemaSetName
194                 anchor == queryAnchorName
195             }
196         where:
197             simplePayloadFilter                    || expectedRecordsCount
198             '{"interfaces": [{"id": "01"}]}'       || 2L
199             '{"interfaces": [{"status": "down"}]}' || 1L
200
201     }
202
203     OffsetDateTime getOffsetDateDate(String dateTimeString) {
204         def localDateTime = LocalDateTime.parse(dateTimeString, ISO_TIMESTAMP_FORMATTER)
205         def localZoneOffset = ZoneOffset.systemDefault().getRules().getOffset(localDateTime)
206         return OffsetDateTime.of(localDateTime, localZoneOffset)
207     }
208 }