Test coverage in lockmanager.sql.pessimistic
[appc.git] / appc-dispatcher / appc-dispatcher-common / lock-manager-lib / lock-manager-impl / src / test / java / org / onap / appc / lockmanager / impl / sql / pessimistic / TestMySqlLockManager.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP : APPC
4  * ================================================================================
5  * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Copyright (C) 2017 Amdocs
8  * ================================================================================
9  * Modifications Copyright (C) 2019 Ericsson
10  * =============================================================================
11  * Licensed under the Apache License, Version 2.0 (the "License");
12  * you may not use this file except in compliance with the License.
13  * You may obtain a copy of the License at
14  * 
15  *      http://www.apache.org/licenses/LICENSE-2.0
16  * 
17  * Unless required by applicable law or agreed to in writing, software
18  * distributed under the License is distributed on an "AS IS" BASIS,
19  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20  * See the License for the specific language governing permissions and
21  * limitations under the License.
22  * 
23  * ============LICENSE_END=========================================================
24  */
25
26 package org.onap.appc.lockmanager.impl.sql.pessimistic;
27
28 import static org.hamcrest.CoreMatchers.isA;
29 import static org.junit.Assert.assertEquals;
30 import static org.junit.Assert.assertNull;
31 import static org.junit.Assert.assertTrue;
32 import java.sql.CallableStatement;
33 import java.sql.Connection;
34 import java.sql.PreparedStatement;
35 import java.sql.ResultSet;
36 import java.sql.SQLException;
37 import java.sql.Timestamp;
38 import java.util.concurrent.Callable;
39 import java.util.concurrent.ExecutionException;
40 import java.util.concurrent.ExecutorService;
41 import java.util.concurrent.Executors;
42 import java.util.concurrent.Future;
43 import java.util.concurrent.TimeUnit;
44 import java.util.concurrent.TimeoutException;
45 import org.junit.Assert;
46 import org.junit.Rule;
47 import org.junit.Test;
48 import org.junit.rules.ExpectedException;
49 import org.mockito.Mockito;
50 import org.onap.appc.dao.util.api.JdbcConnectionFactory;
51 import org.onap.appc.lockmanager.api.LockException;
52 import org.onap.appc.lockmanager.api.LockRuntimeException;
53 import org.onap.appc.lockmanager.impl.sql.JdbcLockManager;
54 import org.onap.appc.lockmanager.impl.sql.MySqlLockManagerBaseTests;
55 import org.onap.appc.lockmanager.impl.sql.Synchronizer;
56
57
58 public class TestMySqlLockManager extends MySqlLockManagerBaseTests {
59
60     private static int CRITICAL_SECTION_WAIT_TIMEOUT = 1; // in secs
61
62     @Override
63     protected JdbcLockManager createJdbcLockManager(boolean useReal) {
64         return new MySqlLockManagerMock(useReal);
65     }
66
67     @Test
68     public void testConcurrentLock() throws LockException, InterruptedException, ExecutionException, TimeoutException {
69         try {
70             callConcurrentTest(new Callable<Boolean>() {
71                 @Override
72                 public Boolean call() throws Exception {
73                     try {
74                         Assert.assertTrue(lockManager.acquireLock(Resource.Resource1.name(), Owner.A.name()));
75                         return true;
76                     } catch(LockRuntimeException e) {
77                         Assert.assertEquals("Cannot obtain critical section lock for resource [" + Resource.Resource1.name() + "].", e.getMessage());
78                         return false;
79                     }
80                 }
81             });
82         } finally {
83             lockManager.releaseLock(Resource.Resource1.name(), Owner.A.name());
84         }
85     }
86
87     @Test
88     public void testConcurrentUnlock() throws LockException, InterruptedException, ExecutionException, TimeoutException {
89         lockManager.acquireLock(Resource.Resource1.name(), Owner.A.name());
90         callConcurrentTest(new Callable<Boolean>() {
91             @Override
92             public Boolean call() throws Exception {
93                 try {
94                     lockManager.releaseLock(Resource.Resource1.name(), Owner.A.name());
95                     return true;
96                 } catch(LockRuntimeException e) {
97                     Assert.assertEquals("Cannot obtain critical section lock for resource [" + Resource.Resource1.name() + "].", e.getMessage());
98                     return false;
99                 }
100             }
101         });
102     }
103
104     private void callConcurrentTest(Callable<Boolean> callable) throws LockException, InterruptedException, ExecutionException, TimeoutException {
105         final int participantsNo = 2;
106         Synchronizer synchronizer = new Synchronizer(participantsNo) {
107
108             @Override
109             protected void waitForAllParticipants(Object waitObj, int totalParticipantsNo, int currentParticipantsNo) {
110                 waitOn(this, TimeUnit.MILLISECONDS.convert(1 + CRITICAL_SECTION_WAIT_TIMEOUT, TimeUnit.SECONDS)); // add 1 sec to make sure timeout occured
111             }
112         };
113         if(!setSynchronizer(synchronizer)) {
114             return;
115         }
116         ((MySqlLockManager)lockManager).setCriticalSectionWaitTimeoutSecs(CRITICAL_SECTION_WAIT_TIMEOUT);
117         ExecutorService executor = Executors.newFixedThreadPool(participantsNo);
118         Future<Boolean> future1 = executor.submit(callable);
119         try {
120             for(int i = 0; i < 10; i++) {
121                 Thread.sleep(100);
122                 if(synchronizer.getParticipantCount() > 0) {
123                     break;
124                 }
125             }
126             // make sure 1st thread gets inside critical section
127             if(synchronizer.getParticipantCount() < 1) {
128                 Assert.fail(getClass().getName() + " first thread failed to acquireLock()");
129             }
130             Future<Boolean> future2 = executor.submit(callable);
131             try {
132                 // 1st thread should acquire the lock
133                 Assert.assertTrue(future1.get(3 + CRITICAL_SECTION_WAIT_TIMEOUT, TimeUnit.SECONDS));
134                 // 2nd thread should fail waiting for critical section
135                 Assert.assertFalse(future2.get(2 + CRITICAL_SECTION_WAIT_TIMEOUT, TimeUnit.SECONDS));
136             } finally {
137                 future2.cancel(true);
138             }
139         } finally {
140             future1.cancel(true);
141             setSynchronizer(null);
142         }
143     }
144
145     @Rule
146     public ExpectedException expectedEx = ExpectedException.none();
147
148     @Test
149     public void testAcquireLockNullOwner() throws LockException {
150         MySqlLockManager lockManager = new MySqlLockManager();
151         expectedEx.expect(LockRuntimeException.class);
152         lockManager.acquireLock(null, null, 0);
153     }
154
155     @Test
156     public void testIsLocked() throws SQLException {
157         MySqlLockManager lockManager = Mockito.spy(new MySqlLockManager());
158         Mockito.doThrow(new SQLException()).when(lockManager).loadLockRecord(Mockito.any(Connection.class), Mockito.anyString());
159         JdbcConnectionFactory connectionFactory = Mockito.mock(JdbcConnectionFactory.class);
160         Mockito.when(connectionFactory.openDbConnection()).thenReturn(Mockito.mock(Connection.class));
161         lockManager.setConnectionFactory(connectionFactory);
162         expectedEx.expect(LockRuntimeException.class);
163         lockManager.isLocked(" ");
164     }
165
166     @Test
167     public void testGetLockOwnerExceptionFlow() throws SQLException {
168         MySqlLockManager lockManager = Mockito.spy(new MySqlLockManager());
169         Mockito.doThrow(new SQLException()).when(lockManager).loadLockRecord(Mockito.any(Connection.class), Mockito.anyString());
170         JdbcConnectionFactory connectionFactory = Mockito.mock(JdbcConnectionFactory.class);
171         Mockito.when(connectionFactory.openDbConnection()).thenReturn(Mockito.mock(Connection.class));
172         lockManager.setConnectionFactory(connectionFactory);
173         expectedEx.expect(LockRuntimeException.class);
174         lockManager.getLockOwner(" ");
175     }
176
177     @Test
178     public void testGetLockOwnerNull() throws SQLException {
179         MySqlLockManager lockManager = Mockito.spy(new MySqlLockManager());
180         Mockito.doReturn(null).when(lockManager).loadLockRecord(Mockito.any(Connection.class), Mockito.anyString());
181         JdbcConnectionFactory connectionFactory = Mockito.mock(JdbcConnectionFactory.class);
182         Mockito.when(connectionFactory.openDbConnection()).thenReturn(Mockito.mock(Connection.class));
183         lockManager.setConnectionFactory(connectionFactory);
184         assertNull(lockManager.getLockOwner(" "));
185     }
186
187     @Test
188     public void testGetLockOwnerExpired() throws SQLException {
189         MySqlLockManager lockManager = Mockito.spy(new MySqlLockManager());
190         LockRecord lockRecord = Mockito.mock(LockRecord.class);
191         Mockito.when(lockRecord.getTimeout()).thenReturn(1L);
192         Mockito.when(lockRecord.getUpdated()).thenReturn(System.currentTimeMillis()-100);
193         Mockito.when(lockRecord.getOwner()).thenReturn("OWNER");
194         Mockito.doReturn(lockRecord).when(lockManager).loadLockRecord(Mockito.any(Connection.class), Mockito.anyString());
195         JdbcConnectionFactory connectionFactory = Mockito.mock(JdbcConnectionFactory.class);
196         Connection connection = Mockito.mock(Connection.class);
197         PreparedStatement statement = Mockito.mock(PreparedStatement.class);
198         ResultSet resultSet = Mockito.mock(ResultSet.class);
199         Mockito.when(resultSet.next()).thenReturn(true);
200         Mockito.when(resultSet.getTimestamp(1)).thenReturn(new Timestamp(System.currentTimeMillis()));
201         Mockito.when(statement.executeQuery()).thenReturn(resultSet);
202         Mockito.when(connection.prepareStatement(SqlLockManager.SQL_CURRENT_TIMESTAMP)).thenReturn(statement);
203         Mockito.when(connectionFactory.openDbConnection()).thenReturn(connection);
204         lockManager.setConnectionFactory(connectionFactory);
205         assertNull(lockManager.getLockOwner(" "));
206     }
207
208     @Test
209     public void testGetLockOwnerNotExpired() throws SQLException {
210         MySqlLockManager lockManager = Mockito.spy(new MySqlLockManager());
211         LockRecord lockRecord = Mockito.mock(LockRecord.class);
212         Mockito.when(lockRecord.getTimeout()).thenReturn(1L);
213         Mockito.when(lockRecord.getUpdated()).thenReturn(System.currentTimeMillis()+10000);
214         Mockito.when(lockRecord.getOwner()).thenReturn("OWNER");
215         Mockito.doReturn(lockRecord).when(lockManager).loadLockRecord(Mockito.any(Connection.class), Mockito.anyString());
216         JdbcConnectionFactory connectionFactory = Mockito.mock(JdbcConnectionFactory.class);
217         Connection connection = Mockito.mock(Connection.class);
218         PreparedStatement statement = Mockito.mock(PreparedStatement.class);
219         ResultSet resultSet = Mockito.mock(ResultSet.class);
220         Mockito.when(resultSet.next()).thenReturn(true);
221         Mockito.when(resultSet.getTimestamp(1)).thenReturn(new Timestamp(System.currentTimeMillis()));
222         Mockito.when(statement.executeQuery()).thenReturn(resultSet);
223         Mockito.when(connection.prepareStatement(SqlLockManager.SQL_CURRENT_TIMESTAMP)).thenReturn(statement);
224         Mockito.when(connectionFactory.openDbConnection()).thenReturn(connection);
225         lockManager.setConnectionFactory(connectionFactory);
226         assertEquals("OWNER", lockManager.getLockOwner(" "));
227     }
228
229     @Test
230     public void testLoadLockRecord() throws SQLException {
231         MySqlLockManager lockManager = Mockito.spy(new MySqlLockManager());
232         lockManager.setTableName("TABLE_NAME");
233         JdbcConnectionFactory connectionFactory = Mockito.mock(JdbcConnectionFactory.class);
234         Connection connection = Mockito.mock(Connection.class);
235         PreparedStatement statement = Mockito.mock(PreparedStatement.class);
236         ResultSet resultSet = Mockito.mock(ResultSet.class);
237         Mockito.when(resultSet.next()).thenReturn(true);
238         Mockito.when(resultSet.getString(2)).thenReturn("OWNER");
239         Mockito.when(resultSet.getLong(3)).thenReturn(0L);
240         Mockito.when(resultSet.getLong(4)).thenReturn(0L);
241         Mockito.when(resultSet.getLong(5)).thenReturn(0L);
242         Mockito.when(statement.executeQuery()).thenReturn(resultSet);
243         Mockito.when(connection.prepareStatement(String.format(SqlLockManager.SQL_LOAD_LOCK_RECORD, "TABLE_NAME"))).thenReturn(statement);
244         Mockito.when(connectionFactory.openDbConnection()).thenReturn(connection);
245         lockManager.setConnectionFactory(connectionFactory);
246         assertTrue(lockManager.loadLockRecord(connection, "") instanceof LockRecord);
247     }
248
249     @Test
250     public void testAddLockRecord() throws SQLException {
251         MySqlLockManager lockManager = Mockito.spy(new MySqlLockManager());
252         lockManager.setTableName("TABLE_NAME");
253         JdbcConnectionFactory connectionFactory = Mockito.mock(JdbcConnectionFactory.class);
254         Connection connection = Mockito.mock(Connection.class);
255         PreparedStatement statement2 = Mockito.mock(PreparedStatement.class);
256         ResultSet resultSet2 = Mockito.mock(ResultSet.class);
257         Mockito.when(resultSet2.next()).thenReturn(true);
258         Mockito.when(resultSet2.getTimestamp(1)).thenReturn(new Timestamp(System.currentTimeMillis()));
259         Mockito.when(statement2.executeQuery()).thenReturn(resultSet2);
260         Mockito.when(connection.prepareStatement(SqlLockManager.SQL_CURRENT_TIMESTAMP)).thenReturn(statement2);
261
262         PreparedStatement statement = Mockito.mock(PreparedStatement.class);
263         ResultSet resultSet = Mockito.mock(ResultSet.class);
264         Mockito.when(resultSet.next()).thenReturn(true);
265         Mockito.when(resultSet.getString(2)).thenReturn("OWNER");
266         Mockito.when(resultSet.getLong(3)).thenReturn(0L);
267         Mockito.when(resultSet.getLong(4)).thenReturn(0L);
268         Mockito.when(resultSet.getLong(5)).thenReturn(0L);
269         Mockito.when(statement.executeQuery()).thenReturn(resultSet);
270         Mockito.when(connection.prepareStatement(String.format(SqlLockManager.SQL_INSERT_LOCK_RECORD, "TABLE_NAME"))).thenReturn(statement);
271         Mockito.when(connectionFactory.openDbConnection()).thenReturn(connection);
272         lockManager.setConnectionFactory(connectionFactory);
273         lockManager.addLockRecord(connection, "", "", 0L);
274         Mockito.verify(statement).executeUpdate();
275     }
276
277     @Test
278     public void testUpdateLockRecord() throws SQLException {
279         MySqlLockManager lockManager = Mockito.spy(new MySqlLockManager());
280         lockManager.setTableName("TABLE_NAME");
281         JdbcConnectionFactory connectionFactory = Mockito.mock(JdbcConnectionFactory.class);
282         Connection connection = Mockito.mock(Connection.class);
283         PreparedStatement statement2 = Mockito.mock(PreparedStatement.class);
284         ResultSet resultSet2 = Mockito.mock(ResultSet.class);
285         Mockito.when(resultSet2.next()).thenReturn(true);
286         Mockito.when(resultSet2.getTimestamp(1)).thenReturn(new Timestamp(-1));
287         Mockito.when(statement2.executeQuery()).thenReturn(resultSet2);
288         Mockito.when(connection.prepareStatement(SqlLockManager.SQL_CURRENT_TIMESTAMP)).thenReturn(statement2);
289
290         PreparedStatement statement = Mockito.mock(PreparedStatement.class);
291         ResultSet resultSet = Mockito.mock(ResultSet.class);
292         Mockito.when(resultSet.next()).thenReturn(true);
293         Mockito.when(resultSet.getString(2)).thenReturn("OWNER");
294         Mockito.when(resultSet.getLong(3)).thenReturn(0L);
295         Mockito.when(resultSet.getLong(4)).thenReturn(0L);
296         Mockito.when(resultSet.getLong(5)).thenReturn(0L);
297         Mockito.when(statement.executeQuery()).thenReturn(resultSet);
298         Mockito.when(connection.prepareStatement(String.format(SqlLockManager.SQL_UPDATE_LOCK_RECORD, "TABLE_NAME"))).thenReturn(statement);
299         Mockito.when(connectionFactory.openDbConnection()).thenReturn(connection);
300         lockManager.setConnectionFactory(connectionFactory);
301         lockManager.updateLockRecord(connection, "", "", 0L);
302         Mockito.verify(statement).executeUpdate();
303     }
304
305     @Test
306     public void testEnterCriticalSectionLockRuntimeException() throws SQLException {
307         MySqlLockManager lockManager = Mockito.spy(new MySqlLockManager());
308         Connection connection = Mockito.mock(Connection.class);
309         CallableStatement callableStatement = Mockito.mock(CallableStatement.class);
310         Mockito.when(connection.prepareCall("SELECT COALESCE(GET_LOCK(?,?),0)")).thenReturn(callableStatement);
311         expectedEx.expect(LockRuntimeException.class);
312         expectedEx.expectMessage("Cannot obtain critical section lock for resource [null].");
313         lockManager.enterCriticalSection(connection, null);
314     }
315
316     @Test
317     public void testEnterCriticalSectionLockRuntimeException2() throws SQLException {
318         MySqlLockManager lockManager = Mockito.spy(new MySqlLockManager());
319         Connection connection = Mockito.mock(Connection.class);
320         Mockito.when(connection.prepareCall("SELECT COALESCE(GET_LOCK(?,?),0)")).thenThrow(new SQLException());
321         expectedEx.expect(LockRuntimeException.class);
322         expectedEx.expectMessage("Cannot obtain critical section lock for resource [null].");
323         expectedEx.expectCause(isA(SQLException.class));
324         lockManager.enterCriticalSection(connection, null);
325     }
326
327     @Test
328     public void testEnterCriticalSection() throws SQLException {
329         MySqlLockManager lockManager = Mockito.spy(new MySqlLockManager());
330         Connection connection = Mockito.mock(Connection.class);
331         CallableStatement callableStatement = Mockito.mock(CallableStatement.class);
332         Mockito.when(callableStatement.execute()).thenReturn(true);
333         ResultSet resultSet = Mockito.mock(ResultSet.class);
334         Mockito.when(resultSet.getInt(1)).thenReturn(1);
335         Mockito.when(resultSet.next()).thenReturn(true);
336         Mockito.when(callableStatement.getResultSet()).thenReturn(resultSet);
337         Mockito.when(connection.prepareCall("SELECT COALESCE(GET_LOCK(?,?),0)")).thenReturn(callableStatement);
338         lockManager.enterCriticalSection(connection, null);
339         Mockito.verify(callableStatement).close();
340     }
341
342     @Test
343     public void testLeaveCriticalSection() throws SQLException {
344         MySqlLockManager lockManager = Mockito.spy(new MySqlLockManager());
345         Connection connection = Mockito.mock(Connection.class);
346         CallableStatement callableStatement = Mockito.mock(CallableStatement.class);
347         Mockito.when(connection.prepareCall("SELECT RELEASE_LOCK(?)")).thenReturn(callableStatement);
348         lockManager.leaveCriticalSection(connection, null);
349         Mockito.verify(callableStatement).close();
350     }
351
352     @Test
353     public void testLeaveCriticalSectionExceptionFlow() throws SQLException {
354         MySqlLockManager lockManager = Mockito.spy(new MySqlLockManager());
355         Connection connection = Mockito.mock(Connection.class);
356         Mockito.when(connection.prepareCall("SELECT RELEASE_LOCK(?)")).thenThrow(new SQLException());
357         expectedEx.expect(LockRuntimeException.class);
358         lockManager.leaveCriticalSection(connection, null);
359         expectedEx.expectMessage("Error releasing critical section lock.");
360         expectedEx.expectCause(isA(SQLException.class));
361     }
362 }