c0d59d91e00a4fa145a7700c4bf53d5f65f87568
[smo/teiv.git] /
1 /*
2  *  ============LICENSE_START=======================================================
3  *  Copyright (C) 2024 Ericsson
4  *  Modifications Copyright (C) 2024 OpenInfra Foundation Europe
5  *  ================================================================================
6  *  Licensed under the Apache License, Version 2.0 (the "License");
7  *  you may not use this file except in compliance with the License.
8  *  You may obtain a copy of the License at
9  *
10  *        http://www.apache.org/licenses/LICENSE-2.0
11  *
12  *  Unless required by applicable law or agreed to in writing, software
13  *  distributed under the License is distributed on an "AS IS" BASIS,
14  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  *  See the License for the specific language governing permissions and
16  *  limitations under the License.
17  *
18  *  SPDX-License-Identifier: Apache-2.0
19  *  ============LICENSE_END=========================================================
20  */
21 package org.oran.smo.teiv.exposure.classifiers.api;
22
23 import static org.assertj.core.api.Assertions.assertThatThrownBy;
24 import static org.jooq.impl.DSL.field;
25 import static org.jooq.impl.DSL.table;
26 import static org.junit.jupiter.api.Assertions.assertEquals;
27 import static org.oran.smo.teiv.utils.TiesConstants.CLASSIFIERS;
28 import static org.oran.smo.teiv.utils.TiesConstants.CONSUMER_DATA_PREFIX;
29 import static org.oran.smo.teiv.utils.TiesConstants.QUOTED_STRING;
30 import static org.oran.smo.teiv.utils.TiesConstants.REL_PREFIX;
31 import static org.oran.smo.teiv.utils.TiesConstants.TIES_CONSUMER_DATA_SCHEMA;
32 import static org.oran.smo.teiv.utils.TiesConstants.TIES_DATA;
33 import static org.oran.smo.teiv.utils.TiesConstants.TIES_DATA_SCHEMA;
34 import static org.oran.smo.teiv.utils.TiesTestConstants.KAFKA_RETRY_INTERVAL_10_MS;
35 import static org.oran.smo.teiv.utils.TiesTestConstants.SPRING_BOOT_SERVER_HOST;
36 import static org.oran.smo.teiv.utils.TiesTestConstants.SPRING_BOOT_SERVER_PORT;
37
38 import com.fasterxml.jackson.databind.ObjectMapper;
39 import org.apache.kafka.clients.consumer.ConsumerConfig;
40 import org.apache.kafka.clients.consumer.KafkaConsumer;
41 import org.jooq.DSLContext;
42 import org.jooq.JSONB;
43 import org.jooq.Record1;
44 import org.jooq.Result;
45 import org.jooq.SQLDialect;
46 import org.jooq.SelectConditionStep;
47 import org.jooq.impl.DSL;
48 import org.junit.jupiter.api.BeforeAll;
49 import org.junit.jupiter.api.BeforeEach;
50 import org.junit.jupiter.api.Test;
51 import org.junit.jupiter.api.TestInstance;
52 import org.oran.smo.teiv.db.TestPostgresqlContainer;
53 import org.oran.smo.teiv.startup.SchemaCleanUpHandler;
54 import org.springframework.beans.factory.annotation.Autowired;
55 import org.springframework.boot.jdbc.DataSourceBuilder;
56 import org.springframework.boot.test.context.SpringBootTest;
57 import org.springframework.boot.test.mock.mockito.MockBean;
58 import org.springframework.kafka.test.context.EmbeddedKafka;
59 import org.springframework.test.annotation.DirtiesContext;
60 import org.springframework.test.annotation.DirtiesContext.ClassMode;
61 import org.springframework.test.context.ActiveProfiles;
62 import org.springframework.test.context.DynamicPropertyRegistry;
63 import org.springframework.test.context.DynamicPropertySource;
64 import org.apache.kafka.common.serialization.StringDeserializer;
65
66 import javax.sql.DataSource;
67 import java.util.Collections;
68 import java.util.List;
69 import java.util.Properties;
70
71 import org.oran.smo.teiv.api.model.OranTeivClassifier;
72 import org.oran.smo.teiv.api.model.OranTeivClassifier.OperationEnum;
73 import org.oran.smo.teiv.exception.TiesException;
74 import org.oran.smo.teiv.schema.PostgresSchemaLoader;
75 import org.oran.smo.teiv.schema.SchemaLoaderException;
76 import org.oran.smo.teiv.startup.SchemaHandler;
77 import org.oran.smo.teiv.utils.JooqTypeConverter;
78
79 @EmbeddedKafka
80 @DirtiesContext(classMode = ClassMode.BEFORE_CLASS)
81 @TestInstance(TestInstance.Lifecycle.PER_CLASS)
82 @ActiveProfiles({ "test", "exposure" })
83 @SpringBootTest(properties = { SPRING_BOOT_SERVER_HOST, SPRING_BOOT_SERVER_PORT, KAFKA_RETRY_INTERVAL_10_MS })
84 class ClassifiersServiceContainerizedTest {
85
86     public static TestPostgresqlContainer postgreSQLContainer = TestPostgresqlContainer.getInstance();
87     private static DSLContext writeDataDslContext;
88     private static DSLContext readDataDslContext;
89
90     private static final String ENTITY_TYPE = "ODUFunction";
91     private static final String TABLE_NAME = String.format(TIES_DATA, "o-ran-smo-teiv-ran_ODUFunction");
92     private static final String ENTITY_ID = "urn:3gpp:dn:SubNetwork=Europe,SubNetwork=Hungary,MeContext=1,ManagedElement=16,ODUFunction=16";
93     private static final String RELATIONSHIP_ID = "urn:o-ran:smo:teiv:sha512:MANAGEDELEMENT_MANAGES_ODUFUNCTION=D67357F682531C7B068486313B0FDAC3E719A166229520196FB9CE917E0236754226A5BCBF7BB7240E516D7ED3FEA852855EC3F121DD4BAFEC5646F2A37F57EE";
94     private static final String RELATIONSHIP_TYPE = "MANAGEDELEMENT_MANAGES_ODUFUNCTION";
95     private static final String ENTITY_CLASSIFIERS = String.format(QUOTED_STRING, CONSUMER_DATA_PREFIX + CLASSIFIERS);
96     private static final String RELATIONSHIP_CLASSIFIERS = String.format(QUOTED_STRING,
97             REL_PREFIX + CONSUMER_DATA_PREFIX + CLASSIFIERS + "_" + RELATIONSHIP_TYPE);
98
99     @Autowired
100     private ClassifiersService classifiersService;
101
102     @Autowired
103     private ObjectMapper objectMapper;
104
105     @MockBean
106     private SchemaHandler schemaHandler;
107
108     @MockBean
109     private SchemaCleanUpHandler schemaCleanUpHandler;
110
111     @DynamicPropertySource
112     static void setProperties(DynamicPropertyRegistry registry) {
113         registry.add("spring.datasource.read.jdbc-url", () -> postgreSQLContainer.getJdbcUrl());
114         registry.add("spring.datasource.read.username", () -> postgreSQLContainer.getUsername());
115         registry.add("spring.datasource.read.password", () -> postgreSQLContainer.getPassword());
116
117         registry.add("spring.datasource.write.jdbc-url", () -> postgreSQLContainer.getJdbcUrl());
118         registry.add("spring.datasource.write.username", () -> postgreSQLContainer.getUsername());
119         registry.add("spring.datasource.write.password", () -> postgreSQLContainer.getPassword());
120     }
121
122     @BeforeAll
123     static void setupAll() throws SchemaLoaderException {
124         String url = postgreSQLContainer.getJdbcUrl();
125         DataSource ds = DataSourceBuilder.create().url(url).username("test").password("test").build();
126         DSLContext dslContext = DSL.using(ds, SQLDialect.POSTGRES);
127         writeDataDslContext = DSL.using(ds, SQLDialect.POSTGRES);
128         readDataDslContext = DSL.using(ds, SQLDialect.POSTGRES);
129         PostgresSchemaLoader postgresSchemaLoader = new PostgresSchemaLoader(dslContext, new ObjectMapper());
130         postgresSchemaLoader.loadSchemaRegistry();
131         TestPostgresqlContainer.loadSampleData();
132     }
133
134     @BeforeEach
135     public void setupEach() {
136         TestPostgresqlContainer.truncateSchemas(List.of(TIES_DATA_SCHEMA, TIES_CONSUMER_DATA_SCHEMA), writeDataDslContext);
137         TestPostgresqlContainer.loadSampleData();
138     }
139
140     @Test
141     void testAdd_entityClassifiers_emptyList() {
142         List<String> classifiersToMerge = Collections.emptyList();
143         List<String> classifiersExpected = List.of("test-app-module:Indoor", "test-app-module:Rural",
144                 "test-app-module:Weekend");
145
146         classifiersService.update(OranTeivClassifier.builder().classifiers(classifiersToMerge).entityIds(List.of(ENTITY_ID))
147                 .relationshipIds(Collections.emptyList()).operation(OperationEnum.MERGE).build());
148
149         verifyClassifiers(ENTITY_CLASSIFIERS, TABLE_NAME, ENTITY_ID, classifiersExpected);
150     }
151
152     @Test
153     void testAdd_entityClassifiers() {
154         List<String> classifiersToMerge = List.of("test-app-module:Outdoor", "test-app-module:Weekday");
155         List<String> classifiersExpected = List.of("test-app-module:Indoor", "test-app-module:Rural",
156                 "test-app-module:Weekend", "test-app-module:Outdoor", "test-app-module:Weekday");
157
158         classifiersService.update(OranTeivClassifier.builder().classifiers(classifiersToMerge).entityIds(List.of(ENTITY_ID))
159                 .relationshipIds(Collections.emptyList()).operation(OperationEnum.MERGE).build());
160
161         verifyClassifiers(ENTITY_CLASSIFIERS, TABLE_NAME, ENTITY_ID, classifiersExpected);
162     }
163
164     @Test
165     void testAdd_relationshipClassifiers() {
166         List<String> classifiersToMerge = List.of("test-app-module:Outdoor", "test-app-module:Weekday");
167         List<String> classifiersExpected = List.of("test-app-module:Indoor", "test-app-module:Rural",
168                 "test-app-module:Weekend", "test-app-module:Outdoor", "test-app-module:Weekday");
169
170         classifiersService.update(OranTeivClassifier.builder().classifiers(classifiersToMerge).entityIds(Collections
171                 .emptyList()).relationshipIds(List.of(RELATIONSHIP_ID)).operation(OperationEnum.MERGE).build());
172
173         verifyClassifiers(RELATIONSHIP_CLASSIFIERS, TABLE_NAME, ENTITY_ID, classifiersExpected);
174     }
175
176     @Test
177     void testAdd_entityAndRelationshipClassifiers() {
178         List<String> classifiersToMerge = List.of("test-app-module:Outdoor", "test-app-module:Weekday");
179         List<String> classifiersExpected = List.of("test-app-module:Indoor", "test-app-module:Rural",
180                 "test-app-module:Weekend", "test-app-module:Outdoor", "test-app-module:Weekday");
181
182         classifiersService.update(OranTeivClassifier.builder().classifiers(classifiersToMerge).entityIds(List.of(ENTITY_ID))
183                 .relationshipIds(List.of(RELATIONSHIP_ID)).operation(OperationEnum.MERGE).build());
184
185         verifyClassifiers(ENTITY_CLASSIFIERS, TABLE_NAME, ENTITY_ID, classifiersExpected);
186         verifyClassifiers(RELATIONSHIP_CLASSIFIERS, TABLE_NAME, ENTITY_ID, classifiersExpected);
187     }
188
189     @Test
190     void testAddDuplicates_entityAndRelationshipClassifiers() {
191         List<String> classifiersToMerge = List.of("test-app-module:Indoor", "test-app-module:Rural");
192         List<String> classifiersExpected = List.of("test-app-module:Indoor", "test-app-module:Rural",
193                 "test-app-module:Weekend");
194
195         classifiersService.update(OranTeivClassifier.builder().classifiers(classifiersToMerge).entityIds(List.of(ENTITY_ID))
196                 .relationshipIds(List.of(RELATIONSHIP_ID)).operation(OperationEnum.MERGE).build());
197
198         verifyClassifiers(ENTITY_CLASSIFIERS, TABLE_NAME, ENTITY_ID, classifiersExpected);
199         verifyClassifiers(RELATIONSHIP_CLASSIFIERS, TABLE_NAME, ENTITY_ID, classifiersExpected);
200     }
201
202     @Test
203     void testAdd_entityAndRelationshipInvalidClassifiers() {
204         List<String> classifiersToMerge = List.of("test-app-module:Indoor_WRONG", "test-app-module:Rural_WRONG");
205
206         assertThatThrownBy(() -> classifiersService.update(OranTeivClassifier.builder().classifiers(classifiersToMerge)
207                 .entityIds(List.of(ENTITY_ID)).relationshipIds(List.of(RELATIONSHIP_ID)).operation(OperationEnum.MERGE)
208                 .build())).isInstanceOf(TiesException.class);
209     }
210
211     @Test
212     void testDelete_emptyEntityClassifiers() {
213         List<String> classifiersToDelete = Collections.emptyList();
214         List<String> classifiersExpected = List.of("test-app-module:Indoor", "test-app-module:Rural",
215                 "test-app-module:Weekend");
216
217         classifiersService.update(OranTeivClassifier.builder().classifiers(classifiersToDelete).entityIds(List.of(
218                 ENTITY_ID)).relationshipIds(Collections.emptyList()).operation(OperationEnum.DELETE).build());
219
220         verifyClassifiers(ENTITY_CLASSIFIERS, TABLE_NAME, ENTITY_ID, classifiersExpected);
221     }
222
223     @Test
224     void testDelete_existingEntityClassifiers() {
225         List<String> classifiersToDelete = List.of("test-app-module:Rural", "test-app-module:Weekend");
226         List<String> classifiersExpected = List.of("test-app-module:Indoor");
227
228         classifiersService.update(OranTeivClassifier.builder().classifiers(classifiersToDelete).entityIds(List.of(
229                 ENTITY_ID)).relationshipIds(Collections.emptyList()).operation(OperationEnum.DELETE).build());
230
231         verifyClassifiers(ENTITY_CLASSIFIERS, TABLE_NAME, ENTITY_ID, classifiersExpected);
232     }
233
234     @Test
235     void testDelete_entityClassifiers_withNotExistingEntityId() {
236         List<String> classifiersToDelete = List.of("test-app-module:Rural", "test-app-module:Weekend");
237
238         assertThatThrownBy(() -> classifiersService.update(OranTeivClassifier.builder().classifiers(classifiersToDelete)
239                 .entityIds(List.of("WRONG_ID")).relationshipIds(Collections.emptyList()).operation(OperationEnum.DELETE)
240                 .build())).isInstanceOf(TiesException.class);
241     }
242
243     @Test
244     void testDelete_existingRelationshipClassifiers() {
245         List<String> classifiersToDelete = List.of("test-app-module:Rural", "test-app-module:Weekend");
246         List<String> classifiersExpected = List.of("test-app-module:Indoor");
247
248         classifiersService.update(OranTeivClassifier.builder().classifiers(classifiersToDelete).entityIds(Collections
249                 .emptyList()).relationshipIds(List.of(RELATIONSHIP_ID)).operation(OperationEnum.DELETE).build());
250
251         verifyClassifiers(RELATIONSHIP_CLASSIFIERS, TABLE_NAME, ENTITY_ID, classifiersExpected);
252     }
253
254     @Test
255     void testDelete_relationshipClassifiers_withNotExistingRelationshipId() {
256         List<String> classifiersToDelete = List.of("test-app-module:Rural", "test-app-module:Weekend");
257
258         assertThatThrownBy(() -> classifiersService.update(OranTeivClassifier.builder().classifiers(classifiersToDelete)
259                 .entityIds(Collections.emptyList()).relationshipIds(List.of("WRONG_ID")).operation(OperationEnum.DELETE)
260                 .build())).isInstanceOf(TiesException.class);
261     }
262
263     @Test
264     void testDelete_existingEntityAndRelationshipClassifiers() {
265         List<String> classifiersToDelete = List.of("test-app-module:Rural", "test-app-module:Weekend");
266         List<String> classifiersExpected = List.of("test-app-module:Indoor");
267
268         classifiersService.update(OranTeivClassifier.builder().classifiers(classifiersToDelete).entityIds(List.of(
269                 ENTITY_ID)).relationshipIds(List.of(RELATIONSHIP_ID)).operation(OperationEnum.DELETE).build());
270
271         verifyClassifiers(ENTITY_CLASSIFIERS, TABLE_NAME, ENTITY_ID, classifiersExpected);
272         verifyClassifiers(RELATIONSHIP_CLASSIFIERS, TABLE_NAME, ENTITY_ID, classifiersExpected);
273     }
274
275     @Test
276     void testDelete_NotExistingEntityAndRelationshipClassifiers() {
277         List<String> classifiersToDelete = List.of("test-app-module:Rural_WRONG", "test-app-module:Weekend_WRONG");
278         List<String> classifiersExpected = List.of("test-app-module:Indoor", "test-app-module:Rural",
279                 "test-app-module:Weekend");
280
281         classifiersService.update(OranTeivClassifier.builder().classifiers(classifiersToDelete).entityIds(List.of(
282                 ENTITY_ID)).relationshipIds(List.of(RELATIONSHIP_ID)).operation(OperationEnum.DELETE).build());
283
284         verifyClassifiers(ENTITY_CLASSIFIERS, TABLE_NAME, ENTITY_ID, classifiersExpected);
285         verifyClassifiers(RELATIONSHIP_CLASSIFIERS, TABLE_NAME, ENTITY_ID, classifiersExpected);
286     }
287
288     private void verifyClassifiers(String fieldName, String tableName, String id, List<String> classifiersExpected) {
289         SelectConditionStep<Record1<JSONB>> select = readDataDslContext.select(field(fieldName, JSONB.class)).from(table(
290                 tableName)).where(field("id").eq(id));
291
292         Result<Record1<JSONB>> result = select.fetch();
293
294         List<String> classifiersActual = JooqTypeConverter.jsonbToList(result.get(0).value1());
295
296         assertEquals(classifiersExpected, classifiersActual);
297     }
298
299     // TODO: create common utility lib
300     private KafkaConsumer<String, String> createConsumerForTest(String server) {
301         Properties properties = new Properties();
302         properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, server);
303         properties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
304         properties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
305         properties.put("auto.offset.reset", "earliest");
306         return new KafkaConsumer<>(properties);
307     }
308 }