f91b58bb9aef0d75880b80bdafd9a558e67e2d3b
[policy/apex-pdp.git] / core / core-engine / src / test / java / org / onap / policy / apex / core / engine / engine / impl / ApexEngineImplTest.java
1 /*-
2  * ============LICENSE_START=======================================================
3  *  Copyright (C) 2018 Ericsson. All rights reserved.
4  *  Modifications Copyright (C) 2019-2020 Nordix Foundation.
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
22 package org.onap.policy.apex.core.engine.engine.impl;
23
24 import static org.assertj.core.api.Assertions.assertThatThrownBy;
25 import static org.awaitility.Awaitility.await;
26 import static org.junit.Assert.assertEquals;
27 import static org.junit.Assert.assertFalse;
28 import static org.junit.Assert.assertNotNull;
29 import static org.junit.Assert.assertNull;
30 import static org.junit.Assert.assertTrue;
31 import static org.junit.Assert.fail;
32
33 import java.io.IOException;
34 import java.lang.reflect.Field;
35 import java.util.HashMap;
36 import java.util.concurrent.TimeUnit;
37
38 import org.junit.AfterClass;
39 import org.junit.Before;
40 import org.junit.BeforeClass;
41 import org.junit.Test;
42 import org.mockito.Mock;
43 import org.mockito.Mockito;
44 import org.mockito.MockitoAnnotations;
45 import org.onap.policy.apex.context.parameters.ContextParameterConstants;
46 import org.onap.policy.apex.context.parameters.DistributorParameters;
47 import org.onap.policy.apex.context.parameters.LockManagerParameters;
48 import org.onap.policy.apex.context.parameters.PersistorParameters;
49 import org.onap.policy.apex.context.parameters.SchemaParameters;
50 import org.onap.policy.apex.core.engine.EngineParameterConstants;
51 import org.onap.policy.apex.core.engine.EngineParameters;
52 import org.onap.policy.apex.core.engine.context.ApexInternalContext;
53 import org.onap.policy.apex.core.engine.event.EnEvent;
54 import org.onap.policy.apex.core.engine.executor.StateMachineExecutor;
55 import org.onap.policy.apex.core.engine.executor.exception.StateMachineException;
56 import org.onap.policy.apex.model.basicmodel.concepts.ApexException;
57 import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
58 import org.onap.policy.apex.model.basicmodel.concepts.AxReferenceKey;
59 import org.onap.policy.apex.model.basicmodel.service.ModelService;
60 import org.onap.policy.apex.model.contextmodel.concepts.AxContextAlbum;
61 import org.onap.policy.apex.model.contextmodel.concepts.AxContextSchema;
62 import org.onap.policy.apex.model.enginemodel.concepts.AxEngineState;
63 import org.onap.policy.apex.model.eventmodel.concepts.AxEvent;
64 import org.onap.policy.apex.model.eventmodel.concepts.AxEvents;
65 import org.onap.policy.apex.model.policymodel.concepts.AxPolicy;
66 import org.onap.policy.apex.model.policymodel.concepts.AxPolicyModel;
67 import org.onap.policy.apex.model.policymodel.concepts.AxState;
68 import org.onap.policy.common.parameters.ParameterService;
69
70 /**
71  * Test the engine implementation.
72  */
73 public class ApexEngineImplTest {
74     private AxPolicyModel policyModel;
75     private AxPolicyModel incompatiblePolicyModel;
76     private AxPolicyModel policyModelWithStates;
77
78     @Mock
79     StateMachineHandler smHandlerMock;
80
81     /**
82      * Set up services.
83      */
84     @BeforeClass
85     public static void setup() {
86         ParameterService.register(new SchemaParameters());
87         ParameterService.register(new DistributorParameters());
88         ParameterService.register(new LockManagerParameters());
89         ParameterService.register(new PersistorParameters());
90         ParameterService.register(new EngineParameters());
91     }
92
93     /**
94      * Set up mocking.
95      */
96     @Before
97     public void initializeMocking() throws ApexException {
98         MockitoAnnotations.initMocks(this);
99
100         Mockito.doThrow(new StateMachineException("mocked state machine exception",
101                         new IOException("nexted exception"))).when(smHandlerMock).execute(Mockito.anyObject());
102     }
103
104     /**
105      * Create policy models.
106      */
107     @Before
108     public void createPolicyModels() {
109         AxArtifactKey modelKey = new AxArtifactKey("PolicyModel:0.0.1");
110         policyModel = new AxPolicyModel(modelKey);
111
112         AxArtifactKey schemaKey = new AxArtifactKey("Schema:0.0.1");
113         AxContextSchema schema = new AxContextSchema(schemaKey, "Java", "java.lang.String");
114         policyModel.getSchemas().getSchemasMap().put(schemaKey, schema);
115
116         AxArtifactKey albumKey = new AxArtifactKey("Album:0.0.1");
117         AxContextAlbum album = new AxContextAlbum(albumKey, "Policy", true, schemaKey);
118
119         policyModel.getAlbums().getAlbumsMap().put(albumKey, album);
120
121         AxEvents events = new AxEvents();
122         AxArtifactKey eventKey = new AxArtifactKey("Event:0.0.1");
123         AxEvent event = new AxEvent(eventKey, "event.name.space", "source", "target");
124         events.getEventMap().put(eventKey, event);
125         policyModel.setEvents(events);
126
127         AxArtifactKey incompatibleModelKey = new AxArtifactKey("IncompatiblePolicyModel:0.0.2");
128         incompatiblePolicyModel = new AxPolicyModel(incompatibleModelKey);
129
130         AxArtifactKey incompatibleSchemaKey = new AxArtifactKey("IncompatibleSchema:0.0.1");
131         AxContextSchema incompatibleSchema = new AxContextSchema(incompatibleSchemaKey, "Java", "java.lang.Integer");
132         incompatiblePolicyModel.getSchemas().getSchemasMap().put(incompatibleSchemaKey, incompatibleSchema);
133
134         AxContextAlbum incompatibleAlbum = new AxContextAlbum(albumKey, "Policy", true, incompatibleSchemaKey);
135         incompatiblePolicyModel.getAlbums().getAlbumsMap().put(albumKey, incompatibleAlbum);
136
137         AxArtifactKey modelKeyStates = new AxArtifactKey("PolicyModelStates:0.0.1");
138         policyModelWithStates = new AxPolicyModel(modelKeyStates);
139         policyModelWithStates.getSchemas().getSchemasMap().put(schemaKey, schema);
140         policyModelWithStates.getAlbums().getAlbumsMap().put(albumKey, album);
141         policyModelWithStates.setEvents(events);
142
143         AxPolicy policy0 = new AxPolicy(new AxArtifactKey("Policy0:0.0.1"));
144         AxState state0 = new AxState(new AxReferenceKey(policy0.getKey(), "state0"));
145         state0.setTrigger(eventKey);
146         policy0.getStateMap().put(state0.getKey().getLocalName(), state0);
147         policy0.setFirstState(state0.getKey().getLocalName());
148
149         policyModelWithStates.getPolicies().getPolicyMap().put(policy0.getKey(), policy0);
150
151         AxPolicy policy1 = new AxPolicy(new AxArtifactKey("Policy1:0.0.1"));
152         AxState state1 = new AxState(new AxReferenceKey(policy1.getKey(), "state1"));
153         state1.setTrigger(eventKey);
154         policy1.getStateMap().put(state1.getKey().getLocalName(), state1);
155         policy1.setFirstState(state1.getKey().getLocalName());
156
157         policyModelWithStates.getPolicies().getPolicyMap().put(policy1.getKey(), policy1);
158     }
159
160     /**
161      * Clear registrations.
162      */
163     @AfterClass
164     public static void teardown() {
165         ParameterService.deregister(ContextParameterConstants.SCHEMA_GROUP_NAME);
166         ParameterService.deregister(ContextParameterConstants.DISTRIBUTOR_GROUP_NAME);
167         ParameterService.deregister(ContextParameterConstants.LOCKING_GROUP_NAME);
168         ParameterService.deregister(ContextParameterConstants.PERSISTENCE_GROUP_NAME);
169         ParameterService.deregister(EngineParameterConstants.MAIN_GROUP_NAME);
170     }
171
172     @Test
173     public void testSanity() throws ApexException {
174         AxArtifactKey engineKey = new AxArtifactKey("Engine:0.0.1");
175         ApexEngineImpl engine = (ApexEngineImpl) new ApexEngineFactory().createApexEngine(engineKey);
176         assertNotNull(engine);
177         assertEquals(engineKey, engine.getKey());
178
179         assertThatThrownBy(() -> engine.start()).hasMessage("start()<-Engine:0.0.1,STOPPED,  cannot start engine, "
180                             + "engine has not been initialized, its model is not loaded");
181
182         assertThatThrownBy(() -> engine.stop())
183             .hasMessage("stop()<-Engine:0.0.1,STOPPED, cannot stop engine, " + "engine is already stopped");
184
185         assertEquals(AxEngineState.STOPPED, engine.getState());
186         assertEquals(0, engine.getEngineContext().size());
187         assertEquals(engineKey, engine.getEngineStatus().getKey());
188         assertNull(engine.getInternalContext());
189
190         engine.clear();
191
192         assertThatThrownBy(() -> engine.addEventListener(null, null))
193             .hasMessage("addEventListener()<-Engine:0.0.1,STOPPED, listenerName is null");
194
195         assertThatThrownBy(() -> engine.addEventListener("myListener", null))
196             .hasMessage("addEventListener()<-Engine:0.0.1,STOPPED, listener is null");
197
198         assertThatThrownBy(() -> engine.removeEventListener(null))
199             .hasMessage("removeEventListener()<-Engine:0.0.1,STOPPED, listenerName is null");
200
201         engine.addEventListener("myListener", new DummyListener());
202         engine.removeEventListener("myListener");
203
204         assertNull(engine.createEvent(null));
205
206         assertFalse(engine.handleEvent(null));
207
208         assertThatThrownBy(() -> engine.updateModel(null, false))
209             .hasMessage("updateModel()<-Engine:0.0.1, Apex model is not defined, it has a null value");
210
211         engine.updateModel(policyModel, false);
212
213         // Force a context exception
214         ModelService.registerModel(AxPolicyModel.class, new AxPolicyModel());
215         assertThatThrownBy(() -> engine.updateModel(incompatiblePolicyModel, false))
216             .hasMessage("updateModel()<-Engine:0.0.1, error setting the context for engine \"Engine:0.0.1\"");
217
218         engine.updateModel(policyModel, false);
219
220         assertNotNull(engine.getInternalContext());
221         assertEquals(1, engine.getEngineContext().size());
222
223         engine.start();
224         assertEquals(AxEngineState.READY, engine.getState());
225
226         assertThatThrownBy(() -> engine.start())
227             .hasMessage("start()<-Engine:0.0.1,READY, cannot start engine, engine not in state STOPPED");
228
229         assertThatThrownBy(() -> engine.clear())
230             .hasMessage("clear()<-Engine:0.0.1,READY, cannot clear engine, engine is not stopped");
231
232         engine.stop();
233         assertEquals(AxEngineState.STOPPED, engine.getState());
234
235         engine.clear();
236         assertEquals(AxEngineState.STOPPED, engine.getState());
237
238         assertThatThrownBy(() -> engine.start()).hasMessage("start()<-Engine:0.0.1,STOPPED,  cannot start engine, "
239             + "engine has not been initialized, its model is not loaded");
240
241         engine.updateModel(policyModel, false);
242         assertEquals(AxEngineState.STOPPED, engine.getState());
243
244         engine.start();
245         assertEquals(AxEngineState.READY, engine.getState());
246
247         assertNull(engine.createEvent(null));
248
249         AxArtifactKey eventKey = new AxArtifactKey("Event:0.0.1");
250         EnEvent event = engine.createEvent(eventKey);
251         assertEquals(eventKey, event.getKey());
252
253         assertTrue(engine.handleEvent(event));
254         assertEquals(AxEngineState.READY, engine.getState());
255
256         engine.stop();
257         assertEquals(AxEngineState.STOPPED, engine.getState());
258
259         engine.addEventListener("myListener", new DummyListener());
260
261         engine.start();
262         assertEquals(AxEngineState.READY, engine.getState());
263
264         assertThatThrownBy(() -> engine.updateModel(policyModel, false)).hasMessage(
265             "updateModel()<-Engine:0.0.1, cannot update model, engine should be stopped but is in state READY");
266
267         assertTrue(engine.handleEvent(event));
268         assertEquals(AxEngineState.READY, engine.getState());
269
270         engine.stop();
271         assertEquals(AxEngineState.STOPPED, engine.getState());
272
273         engine.addEventListener("badListener", new DummyEnEventListener());
274
275         engine.start();
276         assertEquals(AxEngineState.READY, engine.getState());
277
278         assertFalse(engine.handleEvent(event));
279         assertEquals(AxEngineState.READY, engine.getState());
280         engine.stop();
281         assertEquals(AxEngineState.STOPPED, engine.getState());
282
283         engine.removeEventListener("badListener");
284         engine.addEventListener("slowListener", new DummySlowEnEventListener());
285     }
286
287     @Test
288     public void testState() throws InterruptedException, ApexException {
289         AxArtifactKey engineKey = new AxArtifactKey("Engine:0.0.1");
290         ApexEngineImpl engine = (ApexEngineImpl) new ApexEngineFactory().createApexEngine(engineKey);
291         assertNotNull(engine);
292         assertEquals(engineKey, engine.getKey());
293
294         engine.updateModel(policyModel, false);
295         assertEquals(AxEngineState.STOPPED, engine.getState());
296
297         DummySlowEnEventListener slowListener = new DummySlowEnEventListener();
298         engine.addEventListener("slowListener", slowListener);
299
300         engine.start();
301         assertEquals(AxEngineState.READY, engine.getState());
302
303         assertEquals(AxEngineState.READY, engine.getState());
304
305         AxArtifactKey eventKey = new AxArtifactKey("Event:0.0.1");
306         EnEvent event = engine.createEvent(eventKey);
307         assertEquals(eventKey, event.getKey());
308
309         // 1 second is less than the 3 second wait on engine stopping
310         slowListener.setWaitTime(1000);
311         (new Thread() {
312             @Override
313             public void run() {
314                 assertTrue(engine.handleEvent(event));
315                 assertEquals(AxEngineState.STOPPED, engine.getState());
316             }
317         }).start();
318         await().atLeast(50, TimeUnit.MILLISECONDS).until(() -> engine.getState().equals(AxEngineState.EXECUTING));
319         assertEquals(AxEngineState.EXECUTING, engine.getState());
320
321         assertFalse(engine.handleEvent(event));
322         assertNotNull(engine.createEvent(eventKey));
323
324         try {
325             engine.stop();
326             assertEquals(AxEngineState.STOPPED, engine.getState());
327         } catch (ApexException ae) {
328             fail("test should not throw an exception");
329         }
330
331         try {
332             engine.start();
333             assertEquals(AxEngineState.READY, engine.getState());
334         } catch (ApexException ae) {
335             fail("test should not throw an exception");
336         }
337
338         // 4 seconds is more than the 3 second wait on engine stopping
339         slowListener.setWaitTime(4000);
340         (new Thread() {
341             @Override
342             public void run() {
343                 assertTrue(engine.handleEvent(event));
344                 assertEquals(AxEngineState.STOPPED, engine.getState());
345             }
346         }).start();
347
348         await().atLeast(50, TimeUnit.MILLISECONDS).until(() -> engine.getState().equals(AxEngineState.EXECUTING));
349         assertEquals(AxEngineState.EXECUTING, engine.getState());
350         try {
351             engine.stop();
352             assertEquals(AxEngineState.STOPPED, engine.getState());
353             fail("test should throw an exception");
354         } catch (ApexException ae) {
355             assertEquals("stop()<-Engine:0.0.1,STOPPED, error stopping engine, engine stop timed out", ae.getMessage());
356         }
357
358         try {
359             engine.clear();
360             assertEquals(AxEngineState.STOPPED, engine.getState());
361         } catch (ApexException e) {
362             fail("test should not throw an exception");
363         }
364     }
365
366     @Test
367     public void testStateMachineError() throws InterruptedException, IllegalArgumentException, IllegalAccessException,
368                     NoSuchFieldException, SecurityException, ApexException {
369
370         AxArtifactKey engineKey = new AxArtifactKey("Engine:0.0.1");
371         ApexEngineImpl engine = (ApexEngineImpl) new ApexEngineFactory().createApexEngine(engineKey);
372         assertNotNull(engine);
373         assertEquals(engineKey, engine.getKey());
374
375         engine.updateModel(policyModel, false);
376         assertEquals(AxEngineState.STOPPED, engine.getState());
377
378         final Field smHandlerField = engine.getClass().getDeclaredField("stateMachineHandler");
379         smHandlerField.setAccessible(true);
380         smHandlerField.set(engine, smHandlerMock);
381
382         engine.start();
383         assertEquals(AxEngineState.READY, engine.getState());
384
385         assertEquals(AxEngineState.READY, engine.getState());
386
387         AxArtifactKey eventKey = new AxArtifactKey("Event:0.0.1");
388         EnEvent event = engine.createEvent(eventKey);
389         assertEquals(eventKey, event.getKey());
390
391         assertFalse(engine.handleEvent(event));
392         assertEquals(AxEngineState.READY, engine.getState());
393
394         try {
395             engine.stop();
396             assertEquals(AxEngineState.STOPPED, engine.getState());
397         } catch (ApexException ae) {
398             fail("test should not throw an exception");
399         }
400
401         try {
402             Mockito.doThrow(new StateMachineException("mocked state machine exception",
403                             new IOException("nexted exception"))).when(smHandlerMock).start();
404
405             engine.start();
406             fail("test should throw an exception");
407         } catch (ApexException ae) {
408             assertEquals("updateModel()<-Engine:0.0.1, error starting the engine state machines \"Engine:0.0.1\"",
409                             ae.getMessage());
410         }
411
412         assertEquals(AxEngineState.STOPPED, engine.getState());
413
414         try {
415             engine.clear();
416             assertEquals(AxEngineState.STOPPED, engine.getState());
417         } catch (ApexException e) {
418             fail("test should not throw an exception");
419         }
420     }
421
422     @Test
423     public void testStateMachineHandler() throws InterruptedException, IllegalArgumentException, IllegalAccessException,
424                     NoSuchFieldException, SecurityException, ApexException {
425         AxArtifactKey engineKey = new AxArtifactKey("Engine:0.0.1");
426         ApexEngineImpl engine = (ApexEngineImpl) new ApexEngineFactory().createApexEngine(engineKey);
427         assertNotNull(engine);
428         assertEquals(engineKey, engine.getKey());
429
430         engine.updateModel(policyModelWithStates, false);
431         assertEquals(AxEngineState.STOPPED, engine.getState());
432
433         engine.start();
434         assertEquals(AxEngineState.READY, engine.getState());
435
436         AxArtifactKey eventKey = new AxArtifactKey("Event:0.0.1");
437         EnEvent event = engine.createEvent(eventKey);
438         assertEquals(eventKey, event.getKey());
439
440         engine.stop();
441         assertEquals(AxEngineState.STOPPED, engine.getState());
442
443         assertEquals(AxEngineState.STOPPED, engine.getState());
444
445         engine.start();
446         assertEquals(AxEngineState.READY, engine.getState());
447
448         assertEquals(AxEngineState.READY, engine.getState());
449
450         // Can't work, state is not fully defined
451         assertFalse(engine.handleEvent(event));
452         assertEquals(AxEngineState.READY, engine.getState());
453
454         final Field smHandlerField = engine.getClass().getDeclaredField("stateMachineHandler");
455         smHandlerField.setAccessible(true);
456         StateMachineHandler smHandler = (StateMachineHandler) smHandlerField.get(engine);
457
458         final Field smExecutorMapField = smHandler.getClass().getDeclaredField("stateMachineExecutorMap");
459         smExecutorMapField.setAccessible(true);
460         @SuppressWarnings("unchecked")
461         HashMap<AxEvent, StateMachineExecutor> smExMap = (HashMap<AxEvent, StateMachineExecutor>) smExecutorMapField
462                         .get(smHandler);
463
464         assertEquals(1, smExMap.size());
465         DummySmExecutor dummyExecutor = new DummySmExecutor(null, event.getKey());
466         smExMap.put(event.getAxEvent(), dummyExecutor);
467         
468         try {
469             ApexInternalContext internalContext = new ApexInternalContext(policyModelWithStates);
470             dummyExecutor.setContext(null, null, internalContext);
471         } catch (Exception e) {
472             // Ignore this exception, we just need to set the internal context
473         }
474
475         try {
476             engine.stop();
477             assertEquals(AxEngineState.STOPPED, engine.getState());
478         } catch (ApexException ae) {
479             fail("test should not throw an exception");
480         }
481         
482         try {
483             engine.start();
484             fail("test should throw an exception");
485         } catch (ApexException ae) {
486             assertEquals("updateModel()<-Engine:0.0.1, error starting the engine state machines \"Engine:0.0.1\"",
487                             ae.getMessage());
488         }
489
490         assertEquals(AxEngineState.STOPPED, engine.getState());
491
492         try {
493             engine.start();
494             assertEquals(AxEngineState.READY, engine.getState());
495         } catch (ApexException ae) {
496             fail("test should not throw an exception");
497         }
498
499         // Works, Dummy executor fakes event execution
500         assertTrue(engine.handleEvent(event));
501         assertEquals(AxEngineState.READY, engine.getState());
502
503         try {
504             engine.stop();
505             assertEquals(AxEngineState.STOPPED, engine.getState());
506         } catch (ApexException ae) {
507             fail("test should not throw an exception");
508         }
509
510         try {
511             engine.clear();
512             assertEquals(AxEngineState.STOPPED, engine.getState());
513         } catch (ApexException e) {
514             fail("test should not throw an exception");
515         }
516     }
517 }