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
10 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 * SPDX-License-Identifier: Apache-2.0
19 * ============LICENSE_END=========================================================
21 package org.oran.smo.teiv.exposure.classifiers.api;
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;
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;
66 import javax.sql.DataSource;
67 import java.util.Collections;
68 import java.util.List;
69 import java.util.Properties;
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;
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 {
86 public static TestPostgresqlContainer postgreSQLContainer = TestPostgresqlContainer.getInstance();
87 private static DSLContext writeDataDslContext;
88 private static DSLContext readDataDslContext;
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);
100 private ClassifiersService classifiersService;
103 private ObjectMapper objectMapper;
106 private SchemaHandler schemaHandler;
109 private SchemaCleanUpHandler schemaCleanUpHandler;
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());
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());
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();
135 public void setupEach() {
136 TestPostgresqlContainer.truncateSchemas(List.of(TIES_DATA_SCHEMA, TIES_CONSUMER_DATA_SCHEMA), writeDataDslContext);
137 TestPostgresqlContainer.loadSampleData();
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");
146 classifiersService.update(OranTeivClassifier.builder().classifiers(classifiersToMerge).entityIds(List.of(ENTITY_ID))
147 .relationshipIds(Collections.emptyList()).operation(OperationEnum.MERGE).build());
149 verifyClassifiers(ENTITY_CLASSIFIERS, TABLE_NAME, ENTITY_ID, classifiersExpected);
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");
158 classifiersService.update(OranTeivClassifier.builder().classifiers(classifiersToMerge).entityIds(List.of(ENTITY_ID))
159 .relationshipIds(Collections.emptyList()).operation(OperationEnum.MERGE).build());
161 verifyClassifiers(ENTITY_CLASSIFIERS, TABLE_NAME, ENTITY_ID, classifiersExpected);
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");
170 classifiersService.update(OranTeivClassifier.builder().classifiers(classifiersToMerge).entityIds(Collections
171 .emptyList()).relationshipIds(List.of(RELATIONSHIP_ID)).operation(OperationEnum.MERGE).build());
173 verifyClassifiers(RELATIONSHIP_CLASSIFIERS, TABLE_NAME, ENTITY_ID, classifiersExpected);
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");
182 classifiersService.update(OranTeivClassifier.builder().classifiers(classifiersToMerge).entityIds(List.of(ENTITY_ID))
183 .relationshipIds(List.of(RELATIONSHIP_ID)).operation(OperationEnum.MERGE).build());
185 verifyClassifiers(ENTITY_CLASSIFIERS, TABLE_NAME, ENTITY_ID, classifiersExpected);
186 verifyClassifiers(RELATIONSHIP_CLASSIFIERS, TABLE_NAME, ENTITY_ID, classifiersExpected);
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");
195 classifiersService.update(OranTeivClassifier.builder().classifiers(classifiersToMerge).entityIds(List.of(ENTITY_ID))
196 .relationshipIds(List.of(RELATIONSHIP_ID)).operation(OperationEnum.MERGE).build());
198 verifyClassifiers(ENTITY_CLASSIFIERS, TABLE_NAME, ENTITY_ID, classifiersExpected);
199 verifyClassifiers(RELATIONSHIP_CLASSIFIERS, TABLE_NAME, ENTITY_ID, classifiersExpected);
203 void testAdd_entityAndRelationshipInvalidClassifiers() {
204 List<String> classifiersToMerge = List.of("test-app-module:Indoor_WRONG", "test-app-module:Rural_WRONG");
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);
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");
217 classifiersService.update(OranTeivClassifier.builder().classifiers(classifiersToDelete).entityIds(List.of(
218 ENTITY_ID)).relationshipIds(Collections.emptyList()).operation(OperationEnum.DELETE).build());
220 verifyClassifiers(ENTITY_CLASSIFIERS, TABLE_NAME, ENTITY_ID, classifiersExpected);
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");
228 classifiersService.update(OranTeivClassifier.builder().classifiers(classifiersToDelete).entityIds(List.of(
229 ENTITY_ID)).relationshipIds(Collections.emptyList()).operation(OperationEnum.DELETE).build());
231 verifyClassifiers(ENTITY_CLASSIFIERS, TABLE_NAME, ENTITY_ID, classifiersExpected);
235 void testDelete_entityClassifiers_withNotExistingEntityId() {
236 List<String> classifiersToDelete = List.of("test-app-module:Rural", "test-app-module:Weekend");
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);
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");
248 classifiersService.update(OranTeivClassifier.builder().classifiers(classifiersToDelete).entityIds(Collections
249 .emptyList()).relationshipIds(List.of(RELATIONSHIP_ID)).operation(OperationEnum.DELETE).build());
251 verifyClassifiers(RELATIONSHIP_CLASSIFIERS, TABLE_NAME, ENTITY_ID, classifiersExpected);
255 void testDelete_relationshipClassifiers_withNotExistingRelationshipId() {
256 List<String> classifiersToDelete = List.of("test-app-module:Rural", "test-app-module:Weekend");
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);
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");
268 classifiersService.update(OranTeivClassifier.builder().classifiers(classifiersToDelete).entityIds(List.of(
269 ENTITY_ID)).relationshipIds(List.of(RELATIONSHIP_ID)).operation(OperationEnum.DELETE).build());
271 verifyClassifiers(ENTITY_CLASSIFIERS, TABLE_NAME, ENTITY_ID, classifiersExpected);
272 verifyClassifiers(RELATIONSHIP_CLASSIFIERS, TABLE_NAME, ENTITY_ID, classifiersExpected);
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");
281 classifiersService.update(OranTeivClassifier.builder().classifiers(classifiersToDelete).entityIds(List.of(
282 ENTITY_ID)).relationshipIds(List.of(RELATIONSHIP_ID)).operation(OperationEnum.DELETE).build());
284 verifyClassifiers(ENTITY_CLASSIFIERS, TABLE_NAME, ENTITY_ID, classifiersExpected);
285 verifyClassifiers(RELATIONSHIP_CLASSIFIERS, TABLE_NAME, ENTITY_ID, classifiersExpected);
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));
292 Result<Record1<JSONB>> result = select.fetch();
294 List<String> classifiersActual = JooqTypeConverter.jsonbToList(result.get(0).value1());
296 assertEquals(classifiersExpected, classifiersActual);
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);