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
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 * SPDX-License-Identifier: Apache-2.0
18 * ============LICENSE_END=========================================================
21 package org.onap.cps.temporal.repository
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
44 * Test specification for network data repository.
49 @AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
50 class NetworkDataRepositoryImplSpec extends Specification {
52 static final String RELOAD_DATA_FOR_SEARCHING = '/data/network-data-changes.sql'
55 DateTimeFormatter ISO_TIMESTAMP_FORMATTER = DateTimeFormatter.ofPattern('yyyy-MM-dd HH:mm:ss.SSS')
57 def queryDataspaceName = 'DATASPACE-01'
59 def querySchemaSetName = 'SCHEMA-SET-01'
61 def queryAnchorName = 'ANCHOR-01'
64 def observedAscSortOrder = new Sort.Order(Sort.Direction.ASC, 'observed_timestamp')
66 def observedDescSortOrder = new Sort.Order(Sort.Direction.DESC, 'observed_timestamp')
68 def anchorAscSortOrder = new Sort.Order(Sort.Direction.ASC, 'anchor')
71 NetworkDataRepository networkDataRepository
74 TimescaleContainer databaseTestContainer = TimescaleContainer.getInstance()
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)
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
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
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)
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
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)
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
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)
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)
170 scenario | sortOrder || expectedObservedTimestamp | expectedAnchorName
171 'observed timestamp desc' | Sort.by(observedDescSortOrder) || '2021-07-24 00:00:01.000' | 'ANCHOR-02'
173 'observed timestamp desc' | Sort.by(anchorAscSortOrder, observedDescSortOrder) || '2021-07-23 00:00:01.000' | 'ANCHOR-01'
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)
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
197 simplePayloadFilter || expectedRecordsCount
198 '{"interfaces": [{"id": "01"}]}' || 2L
199 '{"interfaces": [{"status": "down"}]}' || 1L
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)