4df04d9cebb571cedcadaf38d771a759294575a8
[policy/drools-pdp.git] /
1 /*
2  * ============LICENSE_START=======================================================
3  * ONAP
4  * ================================================================================
5  * Copyright (C) 2018-2021-2022 AT&T Intellectual Property. All rights reserved.
6  * Modifications Copyright (C) 2024 Nordix Foundation.
7  * ================================================================================
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *      http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  * ============LICENSE_END=========================================================
20  */
21
22 package org.onap.policy.drools.protocol.coders;
23
24 import static org.assertj.core.api.Assertions.assertThat;
25 import static org.assertj.core.api.Assertions.assertThatThrownBy;
26 import static org.junit.jupiter.api.Assertions.assertEquals;
27 import static org.junit.jupiter.api.Assertions.assertFalse;
28 import static org.junit.jupiter.api.Assertions.assertNotNull;
29 import static org.junit.jupiter.api.Assertions.assertNull;
30 import static org.junit.jupiter.api.Assertions.assertSame;
31 import static org.junit.jupiter.api.Assertions.assertThrows;
32 import static org.mockito.ArgumentMatchers.any;
33 import static org.mockito.ArgumentMatchers.anyInt;
34 import static org.mockito.ArgumentMatchers.anyString;
35 import static org.mockito.Mockito.doCallRealMethod;
36 import static org.mockito.Mockito.mock;
37 import static org.mockito.Mockito.when;
38 import static org.onap.policy.common.message.bus.properties.MessageBusProperties.PROPERTY_NOOP_SINK_TOPICS;
39 import static org.onap.policy.drools.system.PolicyEngineConstants.PROPERTY_TOPIC_EVENTS_SUFFIX;
40
41 import com.google.gson.Gson;
42 import com.google.gson.GsonBuilder;
43 import java.io.IOException;
44 import java.nio.file.Paths;
45 import java.util.List;
46 import java.util.Properties;
47 import lombok.AllArgsConstructor;
48 import lombok.Getter;
49 import org.junit.jupiter.api.AfterEach;
50 import org.junit.jupiter.api.BeforeAll;
51 import org.junit.jupiter.api.BeforeEach;
52 import org.junit.jupiter.api.Test;
53 import org.kie.api.builder.ReleaseId;
54 import org.onap.policy.common.message.bus.event.TopicEndpointManager;
55 import org.onap.policy.common.message.bus.event.TopicSink;
56 import org.onap.policy.drools.controller.DroolsController;
57 import org.onap.policy.drools.controller.DroolsControllerConstants;
58 import org.onap.policy.drools.controller.internal.MavenDroolsControllerTest;
59 import org.onap.policy.drools.properties.DroolsPropertyConstants;
60 import org.onap.policy.drools.protocol.coders.EventProtocolCoder.CoderFilters;
61 import org.onap.policy.drools.protocol.coders.TopicCoderFilterConfiguration.CustomGsonCoder;
62 import org.onap.policy.drools.util.KieUtils;
63 import org.slf4j.Logger;
64 import org.slf4j.LoggerFactory;
65 import org.springframework.test.util.ReflectionTestUtils;
66
67 /**
68  * ProtocolCoder Toolset Junits.
69  */
70 public class ProtocolCoderToolsetTest {
71     private static final String JUNIT_PROTOCOL_CODER_ARTIFACT_ID = "protocolcoder";
72     private static final String JUNIT_PROTOCOL_CODER_TOPIC = JUNIT_PROTOCOL_CODER_ARTIFACT_ID;
73     private static final String CONTROLLER_ID = "blah";
74
75     private static final Logger logger = LoggerFactory.getLogger(ProtocolCoderToolsetTest.class);
76
77     private static volatile ReleaseId releaseId;
78
79     // customCoder has to be public to be accessed in tests below
80     public static final Gson customCoder = new GsonBuilder().create(); // NOSONAR actually being used in the test
81
82     private DroolsController controller;
83
84     /**
85      * Test Class Initialization.
86      */
87     @BeforeAll
88     public static void setUpClass() throws IOException {
89         releaseId = KieUtils.installArtifact(Paths.get(MavenDroolsControllerTest.JUNIT_ECHO_KMODULE_PATH).toFile(),
90             Paths.get(MavenDroolsControllerTest.JUNIT_ECHO_KMODULE_POM_PATH).toFile(),
91             MavenDroolsControllerTest.JUNIT_ECHO_KJAR_DRL_PATH,
92             Paths.get(MavenDroolsControllerTest.JUNIT_ECHO_KMODULE_DRL_PATH).toFile());
93     }
94
95     /**
96      * Test Set Up.
97      */
98     @BeforeEach
99     public void setUp() {
100         controller = createController();
101     }
102
103     /**
104      * Test Termination.
105      */
106     @AfterEach
107     public void tearDown() {
108         if (controller != null) {
109             DroolsControllerConstants.getFactory().destroy(controller);
110         }
111     }
112
113     @Test
114     void testToolsets() {
115         testGsonToolset(createFilterSet());
116     }
117
118     @Test
119     void testExceptions() {
120         // should fail without params
121         assertThrows(IllegalArgumentException.class,
122             () -> new GsonProtocolCoderToolset(null, "controller"));
123
124         // should fail without controller ID
125         assertThrows(IllegalArgumentException.class,
126             () -> new GsonProtocolCoderToolset(mock(EventProtocolParams.class), ""));
127
128         // set mock under test - always call real method under test
129         var toolset = mock(GsonProtocolCoderToolset.class);
130         when(toolset.getCoder("")).thenCallRealMethod();
131
132         // should fail calling with empty classname
133         assertThatThrownBy(() -> toolset.getCoder(""))
134             .isInstanceOf(IllegalArgumentException.class)
135             .hasMessageContaining("no classname provided");
136
137         // should fail when trying to add coder with empty event class
138         doCallRealMethod().when(toolset).addCoder(anyString(), any(JsonProtocolFilter.class), anyInt());
139         assertThatThrownBy(() -> toolset.addCoder("", mock(JsonProtocolFilter.class), 1))
140             .isInstanceOf(IllegalArgumentException.class)
141             .hasMessageContaining("no event class provided");
142
143         // should fail when trying to remove coder with empty event
144         doCallRealMethod().when(toolset).removeCoders(anyString());
145         assertThatThrownBy(() -> toolset.removeCoders(""))
146             .isInstanceOf(IllegalArgumentException.class)
147             .hasMessageContaining("no event class provided");
148
149         // set coders to empty list
150         when(toolset.filter(anyString())).thenCallRealMethod();
151         ReflectionTestUtils.setField(toolset, "coders", List.of());
152
153         // should fail when trying to find a filter from an empty list
154         assertThatThrownBy(() -> toolset.filter(""))
155             .isInstanceOf(IllegalStateException.class)
156             .hasMessageContaining("No coders available");
157
158         // there is a coder in the list, but can't check if accepts json when coder doesn't have filter
159         var mockCoderFilter = mock(CoderFilters.class);
160         when(mockCoderFilter.getFilter()).thenReturn(null);
161         ReflectionTestUtils.setField(toolset, "coders", List.of(mockCoderFilter));
162
163         assertNull(toolset.filter("json"));
164
165     }
166
167     /**
168      * Test the Gson toolset.
169      *
170      * @param protocolFilter protocol filter
171      */
172     private void testGsonToolset(JsonProtocolFilter protocolFilter) {
173         GsonProtocolCoderToolset gsonToolset =
174             new GsonProtocolCoderToolset(EventProtocolParams.builder().topic(JUNIT_PROTOCOL_CODER_TOPIC)
175                 .groupId(releaseId.getGroupId()).artifactId(releaseId.getArtifactId())
176                 .eventClass(ThreeStrings.class.getName()).protocolFilter(protocolFilter)
177                 .customGsonCoder(null).modelClassLoaderHash(12345678).build(), CONTROLLER_ID);
178
179         assertNotNull(gsonToolset.getEncoder());
180         assertNotNull(gsonToolset.getDecoder());
181         assertThat(gsonToolset.toString()).startsWith("GsonProtocolCoderToolset [");
182         assertThat(gsonToolset.toString()).contains("=ProtocolCoderToolset [");
183         assertThat(gsonToolset.toString()).contains("[CoderFilters [");
184
185         testToolset(protocolFilter, gsonToolset);
186
187         ThreeStrings triple = createTriple();
188         gsonToolset.setCustomCoder(new CustomGsonCoder(this.getClass().getName(), "customCoder"));
189         String tripleEncoded = encode(gsonToolset, triple);
190         decode(protocolFilter, gsonToolset, triple, tripleEncoded);
191     }
192
193     private ThreeStrings createTriple() {
194         return new ThreeStrings("v1", "v2", "v3");
195     }
196
197     private void testToolset(JsonProtocolFilter protocolFilter, ProtocolCoderToolset coderToolset) {
198
199         validateInitialization(protocolFilter, coderToolset);
200
201         updateCoderFilterRule(coderToolset);
202
203         addRemoveCoder(coderToolset);
204
205         /* restore original filters */
206         coderToolset.addCoder(ThreeStrings.class.getName(), protocolFilter, 654321);
207
208         ThreeStrings triple = createTriple();
209
210         String tripleEncoded = encode(coderToolset, triple);
211
212         decode(protocolFilter, coderToolset, triple, tripleEncoded);
213     }
214
215     private void decode(JsonProtocolFilter protocolFilter, ProtocolCoderToolset coderToolset,
216                         ThreeStrings triple, String tripleEncoded) {
217
218         try {
219             coderToolset.decode(tripleEncoded);
220         } catch (UnsupportedOperationException e) {
221             /* OK */
222             logger.trace("Junit expected exception - decode does not pass filtering", e);
223         }
224
225         CoderFilters coderFilters = coderToolset.getCoder(ThreeStrings.class.getName());
226         assertSame(coderFilters.getFactClass(), ThreeStrings.class.getName());
227         assertSame(coderFilters.getFilter(), protocolFilter);
228         assertNotNull(coderFilters.getFilter().getRule());
229
230         coderFilters.getFilter().setRule("[?($.second =~ /^v2$/ && $.third =~ /.*v3.*/)]");
231
232         ThreeStrings tripleDecoded = (ThreeStrings) coderToolset.decode(tripleEncoded);
233
234         assertEquals(triple.getFirst(), tripleDecoded.getFirst());
235         assertEquals(triple.getSecond(), tripleDecoded.getSecond());
236         assertEquals(triple.getThird(), tripleDecoded.getThird());
237
238         coderFilters.getFilter().setRule(null);
239         assertEquals("[?($ =~ /.*/)]", coderFilters.getFilter().getRule());
240
241         tripleDecoded = (ThreeStrings) coderToolset.decode(tripleEncoded);
242
243         assertEquals(tripleDecoded.getFirst(), triple.getFirst());
244         assertEquals(tripleDecoded.getSecond(), triple.getSecond());
245         assertEquals(tripleDecoded.getThird(), triple.getThird());
246
247         coderFilters.getFilter().setRule("[?($.third =~ /.*v3.*/)]");
248     }
249
250     private String encode(ProtocolCoderToolset coderToolset, ThreeStrings triple) {
251         String tripleEncoded = coderToolset.encode(triple);
252         assertFalse(tripleEncoded.isEmpty());
253         return tripleEncoded;
254     }
255
256     private void addRemoveCoder(ProtocolCoderToolset coderToolset) {
257         coderToolset.addCoder(this.getClass().getName(), new JsonProtocolFilter("[?($.second =~ /.*/)]"), 654321);
258         assertEquals(2, coderToolset.getCoders().size());
259
260         coderToolset.removeCoders(this.getClass().getName());
261         assertEquals(1, coderToolset.getCoders().size());
262     }
263
264     private void updateCoderFilterRule(ProtocolCoderToolset coderToolset) {
265         coderToolset.addCoder(ThreeStrings.class.getName(), new JsonProtocolFilter("[?($.third =~ /.*/)]"), 654321);
266
267         assertEquals(1, coderToolset.getCoders().size());
268
269         assertEquals(654321, coderToolset.getCoder(ThreeStrings.class.getName()).getModelClassLoaderHash());
270
271         assertNotNull(coderToolset.getCoder(ThreeStrings.class.getName()).getFilter().getRule());
272
273         assertEquals("[?($.third =~ /.*/)]",
274             coderToolset.getCoder(ThreeStrings.class.getName()).getFilter().getRule());
275     }
276
277     private void validateInitialization(JsonProtocolFilter protocolFilter, ProtocolCoderToolset coderToolset) {
278         assertEquals(CONTROLLER_ID, coderToolset.getControllerId());
279         assertEquals(releaseId.getGroupId(), coderToolset.getGroupId());
280         assertEquals(releaseId.getArtifactId(), coderToolset.getArtifactId());
281         assertNull(coderToolset.getCustomCoder());
282
283         assertEquals(1, coderToolset.getCoders().size());
284
285         CoderFilters coderFilters = coderToolset.getCoder(CONTROLLER_ID);
286         assertNull(coderFilters);
287
288         coderFilters = coderToolset.getCoder(ThreeStrings.class.getName());
289         assertNotNull(coderFilters);
290
291         assertEquals(coderFilters.getFilter(), protocolFilter);
292     }
293
294     private DroolsController createController() {
295         if (releaseId == null) {
296             throw new IllegalStateException("no prereq artifact installed in maven repository");
297         }
298
299         Properties sinkConfig = new Properties();
300         sinkConfig.put(PROPERTY_NOOP_SINK_TOPICS, JUNIT_PROTOCOL_CODER_TOPIC);
301         final List<TopicSink> noopTopics = TopicEndpointManager.getManager().addTopicSinks(sinkConfig);
302
303         Properties droolsControllerConfig = getDroolsControllerConfig();
304
305         return DroolsControllerConstants.getFactory().build(droolsControllerConfig, null, noopTopics);
306     }
307
308     private static Properties getDroolsControllerConfig() {
309         Properties droolsControllerConfig = new Properties();
310         droolsControllerConfig.put(DroolsPropertyConstants.RULES_GROUPID, releaseId.getGroupId());
311         droolsControllerConfig.put(DroolsPropertyConstants.RULES_ARTIFACTID, releaseId.getArtifactId());
312         droolsControllerConfig.put(DroolsPropertyConstants.RULES_VERSION, releaseId.getVersion());
313         droolsControllerConfig.put(
314             PROPERTY_NOOP_SINK_TOPICS + "." + JUNIT_PROTOCOL_CODER_TOPIC
315                 + PROPERTY_TOPIC_EVENTS_SUFFIX,
316             ThreeStrings.class.getName());
317         return droolsControllerConfig;
318     }
319
320     private JsonProtocolFilter createFilterSet() {
321         return new JsonProtocolFilter("[?($.first =~ /.*/ && $.second =~ /^blah.*/ && $.third =~ /^hello$/)]");
322     }
323
324     /**
325      * Note: We need an object that can be constructed, but the apache Triple cannot, thus
326      * we create our own class just for these tests.
327      */
328     @Getter
329     @AllArgsConstructor
330     public static class ThreeStrings {
331         private String first;
332         private String second;
333         private String third;
334     }
335 }