Removing deprecated DMAAP library
[policy/drools-pdp.git] / policy-management / src / test / java / org / onap / policy / drools / system / internal / FeatureLockImplTest.java
1 /*
2  * ============LICENSE_START=======================================================
3  * ONAP
4  * ================================================================================
5  * Copyright (C) 2019-2021 AT&T Intellectual Property. All rights reserved.
6  * Modifications Copyright (C) 2023-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.system.internal;
23
24 import static org.assertj.core.api.Assertions.assertThat;
25 import static org.assertj.core.api.Assertions.assertThatCode;
26 import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
27 import static org.junit.jupiter.api.Assertions.assertEquals;
28 import static org.junit.jupiter.api.Assertions.assertFalse;
29 import static org.junit.jupiter.api.Assertions.assertNotNull;
30 import static org.junit.jupiter.api.Assertions.assertNull;
31 import static org.junit.jupiter.api.Assertions.assertSame;
32 import static org.junit.jupiter.api.Assertions.assertTrue;
33 import static org.mockito.ArgumentMatchers.any;
34 import static org.mockito.Mockito.mock;
35 import static org.mockito.Mockito.never;
36 import static org.mockito.Mockito.times;
37 import static org.mockito.Mockito.verify;
38
39 import java.io.ByteArrayInputStream;
40 import java.io.ByteArrayOutputStream;
41 import java.io.ObjectInputStream;
42 import java.io.ObjectOutputStream;
43 import java.io.Serial;
44 import java.util.concurrent.ScheduledExecutorService;
45 import org.junit.jupiter.api.AfterAll;
46 import org.junit.jupiter.api.AfterEach;
47 import org.junit.jupiter.api.BeforeAll;
48 import org.junit.jupiter.api.BeforeEach;
49 import org.junit.jupiter.api.Test;
50 import org.junit.jupiter.api.extension.ExtendWith;
51 import org.mockito.ArgumentCaptor;
52 import org.mockito.Mock;
53 import org.mockito.MockitoAnnotations;
54 import org.mockito.junit.jupiter.MockitoExtension;
55 import org.onap.policy.drools.core.DroolsRunnable;
56 import org.onap.policy.drools.core.PolicySession;
57 import org.onap.policy.drools.core.lock.LockCallback;
58 import org.onap.policy.drools.core.lock.LockState;
59 import org.onap.policy.drools.system.PolicyEngineConstants;
60 import org.springframework.test.util.ReflectionTestUtils;
61
62 @ExtendWith(MockitoExtension.class)
63 class FeatureLockImplTest {
64     private static final String POLICY_ENGINE_EXECUTOR_FIELD = "executorService";
65     private static final String OWNER_KEY = "my key";
66     private static final String RESOURCE = "my resource";
67     private static final int HOLD_SEC = 100;
68     private static final int HOLD_SEC2 = 120;
69
70     private static ScheduledExecutorService saveExec;
71
72     @Mock
73     private ScheduledExecutorService exsvc;
74
75     @Mock
76     private LockCallback callback;
77
78     AutoCloseable closeable;
79
80     /**
81      * Saves static fields and configures the location of the property files.
82      */
83     @BeforeAll
84     static void setUpBeforeClass() {
85         saveExec = (ScheduledExecutorService) ReflectionTestUtils.getField(PolicyEngineConstants.getManager(),
86             POLICY_ENGINE_EXECUTOR_FIELD);
87     }
88
89     /**
90      * Restores static fields.
91      */
92     @AfterAll
93     static void tearDownAfterClass() {
94         ReflectionTestUtils.setField(PolicyEngineConstants.getManager(), POLICY_ENGINE_EXECUTOR_FIELD, saveExec);
95     }
96
97     /**
98      * Initializes the mocks and creates a feature that uses {@link #exsvc} to execute
99      * tasks.
100      */
101     @BeforeEach
102     void setUp() {
103         closeable = MockitoAnnotations.openMocks(this);
104         ReflectionTestUtils.setField(PolicyEngineConstants.getManager(), POLICY_ENGINE_EXECUTOR_FIELD, exsvc);
105     }
106
107     @AfterEach
108     void closeMocks() throws Exception {
109         closeable.close();
110     }
111
112     @Test
113     void testNoArgs() {
114         MyLock lock = new MyLock();
115         assertNull(lock.getResourceId());
116         assertNull(lock.getOwnerKey());
117         assertNull(lock.getCallback());
118         assertEquals(0, lock.getHoldSec());
119     }
120
121     @Test
122     void testFeatureLockImpl() {
123         MyLock lock = new MyLock(LockState.WAITING, RESOURCE, OWNER_KEY, HOLD_SEC, callback);
124         assertTrue(lock.isWaiting());
125         assertEquals(RESOURCE, lock.getResourceId());
126         assertEquals(OWNER_KEY, lock.getOwnerKey());
127         assertSame(callback, lock.getCallback());
128         assertEquals(HOLD_SEC, lock.getHoldSec());
129     }
130
131     @Test
132     void testSerializable() throws Exception {
133         MyLock lock = new MyLock(LockState.WAITING, RESOURCE, OWNER_KEY, HOLD_SEC, callback);
134         lock = roundTrip(lock);
135
136         assertTrue(lock.isWaiting());
137
138         assertEquals(RESOURCE, lock.getResourceId());
139         assertEquals(OWNER_KEY, lock.getOwnerKey());
140         assertNull(lock.getCallback());
141         assertEquals(HOLD_SEC, lock.getHoldSec());
142     }
143
144     @Test
145     void testGrant() {
146         MyLock lock = new MyLock(LockState.WAITING, RESOURCE, OWNER_KEY, HOLD_SEC, callback);
147         lock.grant();
148
149         assertTrue(lock.isActive());
150         assertEquals(1, lock.nupdates);
151
152         invokeCallback();
153         verify(callback).lockAvailable(any());
154         verify(callback, never()).lockUnavailable(any());
155     }
156
157     /**
158      * Tests grant() when the lock is already unavailable.
159      */
160     @Test
161     void testGrantUnavailable() {
162         MyLock lock = new MyLock(LockState.UNAVAILABLE, RESOURCE, OWNER_KEY, HOLD_SEC, callback);
163         lock.setState(LockState.UNAVAILABLE);
164         lock.grant();
165
166         assertTrue(lock.isUnavailable());
167         assertEquals(0, lock.nupdates);
168
169         verify(exsvc, never()).execute(any());
170     }
171
172     @Test
173     void testDeny() {
174         MyLock lock = new MyLock(LockState.WAITING, RESOURCE, OWNER_KEY, HOLD_SEC, callback);
175         lock.deny("my reason");
176
177         assertTrue(lock.isUnavailable());
178
179         invokeCallback();
180         verify(callback, never()).lockAvailable(any());
181         verify(callback).lockUnavailable(any());
182     }
183
184     /**
185      * Tests doNotify() when a session exists.
186      */
187     @Test
188     void testDoNotifySession() {
189         PolicySession session = mock(PolicySession.class);
190
191         MyLock lock = new MyLock(LockState.WAITING, RESOURCE, OWNER_KEY, HOLD_SEC, callback) {
192             @Serial
193             private static final long serialVersionUID = 1L;
194
195             @Override
196             protected PolicySession getSession() {
197                 return session;
198             }
199         };
200
201         lock.grant();
202
203         assertTrue(lock.isActive());
204         assertEquals(1, lock.nupdates);
205
206         verify(exsvc, never()).execute(any());
207
208         ArgumentCaptor<Object> captor = ArgumentCaptor.forClass(Object.class);
209         verify(session).insertDrools(captor.capture());
210
211         DroolsRunnable runner = (DroolsRunnable) captor.getValue();
212         runner.run();
213
214         verify(callback).lockAvailable(any());
215         verify(callback, never()).lockUnavailable(any());
216     }
217
218     /**
219      * Tests doNotify() when there is no session.
220      */
221     @Test
222     void testDoNotifyNoSession() {
223         MyLock lock = new MyLock(LockState.WAITING, RESOURCE, OWNER_KEY, HOLD_SEC, callback);
224         lock.grant();
225
226         assertTrue(lock.isActive());
227         assertEquals(1, lock.nupdates);
228
229         invokeCallback();
230         verify(callback).lockAvailable(any());
231         verify(callback, never()).lockUnavailable(any());
232     }
233
234     @Test
235     void testFreeAllowed() {
236         MyLock lock = new MyLock(LockState.WAITING, RESOURCE, OWNER_KEY, HOLD_SEC, callback);
237         assertTrue(lock.freeAllowed());
238     }
239
240     /**
241      * Tests freeAllowed() when the lock is unavailable.
242      */
243     @Test
244     void testFreeAllowedUnavailable() {
245         MyLock lock = new MyLock(LockState.UNAVAILABLE, RESOURCE, OWNER_KEY, HOLD_SEC, callback);
246         assertFalse(lock.freeAllowed());
247         assertTrue(lock.isUnavailable());
248     }
249
250     /**
251      * Tests that free() works on a serialized lock with a new feature.
252      *
253      * @throws Exception if an error occurs
254      */
255     @Test
256     void testFreeAllowedSerialized() throws Exception {
257         MyLock lock = new MyLock(LockState.WAITING, RESOURCE, OWNER_KEY, HOLD_SEC, callback);
258
259         lock = roundTrip(lock);
260         assertTrue(lock.freeAllowed());
261     }
262
263     /**
264      * Tests free() on a serialized lock without a feature.
265      *
266      * @throws Exception if an error occurs
267      */
268     @Test
269     void testFreeAllowedNoFeature() throws Exception {
270         MyLock lock = new MyLockNoFeature(LockState.WAITING, RESOURCE, OWNER_KEY, HOLD_SEC, callback);
271
272         lock = roundTrip(lock);
273         assertFalse(lock.freeAllowed());
274         assertTrue(lock.isUnavailable());
275     }
276
277     @Test
278     void testExtendAllowed() {
279         MyLock lock = new MyLock(LockState.WAITING, RESOURCE, OWNER_KEY, HOLD_SEC, callback);
280
281         LockCallback scallback = mock(LockCallback.class);
282         assertTrue(lock.extendAllowed(HOLD_SEC2, scallback));
283         assertTrue(lock.isWaiting());
284         assertEquals(HOLD_SEC2, lock.getHoldSec());
285         assertSame(scallback, lock.getCallback());
286
287         verify(exsvc, never()).execute(any());
288
289         // invalid arguments
290
291         // @formatter:off
292         assertThatIllegalArgumentException().isThrownBy(
293             () -> new MyLock(LockState.WAITING, RESOURCE, OWNER_KEY, HOLD_SEC, callback)
294                             .extendAllowed(-1, callback))
295             .withMessageContaining("holdSec");
296         // @formatter:on
297     }
298
299     /**
300      * Tests extendAllowed() when the lock is unavailable.
301      */
302     @Test
303     void testExtendAllowedUnavailable() {
304         MyLock lock = new MyLock(LockState.UNAVAILABLE, RESOURCE, OWNER_KEY, HOLD_SEC, callback);
305
306         LockCallback scallback = mock(LockCallback.class);
307         assertFalse(lock.extendAllowed(HOLD_SEC2, scallback));
308         assertTrue(lock.isUnavailable());
309         assertEquals(HOLD_SEC2, lock.getHoldSec());
310         assertSame(scallback, lock.getCallback());
311
312         invokeCallback();
313         verify(scallback, never()).lockAvailable(lock);
314         verify(scallback).lockUnavailable(lock);
315     }
316
317     /**
318      * Tests that extendAllowed() works on a serialized lock with a new feature.
319      *
320      * @throws Exception if an error occurs
321      */
322     @Test
323     void testExtendAllowedSerialized() throws Exception {
324         MyLock lock = new MyLock(LockState.WAITING, RESOURCE, OWNER_KEY, HOLD_SEC, callback);
325
326         lock = roundTrip(lock);
327
328         LockCallback scallback = mock(LockCallback.class);
329         assertTrue(lock.extendAllowed(HOLD_SEC2, scallback));
330         assertTrue(lock.isWaiting());
331         assertEquals(HOLD_SEC2, lock.getHoldSec());
332         assertSame(scallback, lock.getCallback());
333
334         verify(exsvc, never()).execute(any());
335     }
336
337     /**
338      * Tests extendAllowed() on a serialized lock without a feature.
339      *
340      * @throws Exception if an error occurs
341      */
342     @Test
343     void testExtendAllowedNoFeature() throws Exception {
344         MyLock lock = new MyLockNoFeature(LockState.WAITING, RESOURCE, OWNER_KEY, HOLD_SEC, callback);
345
346         lock = roundTrip(lock);
347
348         LockCallback scallback = mock(LockCallback.class);
349         assertFalse(lock.extendAllowed(HOLD_SEC2, scallback));
350         assertTrue(lock.isUnavailable());
351         assertEquals(HOLD_SEC2, lock.getHoldSec());
352         assertSame(scallback, lock.getCallback());
353
354         invokeCallback();
355         verify(scallback, never()).lockAvailable(lock);
356         verify(scallback).lockUnavailable(lock);
357     }
358
359     @Test
360     void testGetSession() {
361         MyLockStdSession lock = new MyLockStdSession(LockState.WAITING, RESOURCE, OWNER_KEY, HOLD_SEC, callback);
362
363         // this should invoke the real policy session without throwing an exception
364         assertThatCode(lock::grant).doesNotThrowAnyException();
365     }
366
367     @Test
368     void testToString() {
369         String text = new MyLock(LockState.WAITING, RESOURCE, OWNER_KEY, HOLD_SEC, callback).toString();
370         assertNotNull(text);
371         assertThat(text).contains("LockImpl");
372     }
373
374     private MyLock roundTrip(MyLock lock) throws Exception {
375         ByteArrayOutputStream baos = new ByteArrayOutputStream();
376         try (ObjectOutputStream oos = new ObjectOutputStream(baos)) {
377             oos.writeObject(lock);
378         }
379
380         ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
381         try (ObjectInputStream ois = new ObjectInputStream(bais)) {
382             return (MyLock) ois.readObject();
383         }
384     }
385
386     /**
387      * Invokes the last call-back in the work queue.
388      *
389      */
390     private void invokeCallback() {
391         ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class);
392         verify(exsvc, times(1)).execute(captor.capture());
393         captor.getAllValues().get(0).run();
394     }
395
396     /**
397      * Lock that inherits the normal getSession() method.
398      */
399     public static class MyLockStdSession extends FeatureLockImpl {
400         @Serial
401         private static final long serialVersionUID = 1L;
402         protected int nupdates = 0;
403
404         public MyLockStdSession() {
405             super();
406         }
407
408         public MyLockStdSession(LockState state, String resourceId, String ownerKey, int holdSec,
409             LockCallback callback) {
410             super(state, resourceId, ownerKey, holdSec, callback);
411         }
412
413         @Override
414         protected void updateGrant() {
415             super.updateGrant();
416             ++nupdates;
417         }
418
419         @Override
420         public boolean free() {
421             return false;
422         }
423
424         @Override
425         public void extend(int holdSec, LockCallback callback) {
426             // do nothing
427         }
428
429         @Override
430         protected boolean addToFeature() {
431             return true;
432         }
433     }
434
435     public static class MyLock extends MyLockStdSession {
436         @Serial
437         private static final long serialVersionUID = 1L;
438
439         public MyLock() {
440             super();
441         }
442
443         public MyLock(LockState state, String resourceId, String ownerKey, int holdSec, LockCallback callback) {
444             super(state, resourceId, ownerKey, holdSec, callback);
445         }
446
447         @Override
448         protected PolicySession getSession() {
449             return null;
450         }
451     }
452
453     public static class MyLockNoFeature extends MyLock {
454         @Serial
455         private static final long serialVersionUID = 1L;
456
457         public MyLockNoFeature() {
458             super();
459         }
460
461         public MyLockNoFeature(LockState state, String resourceId, String ownerKey, int holdSec,
462             LockCallback callback) {
463             super(state, resourceId, ownerKey, holdSec, callback);
464         }
465
466         @Override
467         protected boolean addToFeature() {
468             return false;
469         }
470     }
471 }