fd1790d49f895b0c8ebe35a9c050c78420f8c636
[appc.git] /
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  * Licensed under the Apache License, Version 2.0 (the "License");
10  * you may not use this file except in compliance with the License.
11  * You may obtain a copy of the License at
12  * 
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  * 
15  * Unless required by applicable law or agreed to in writing, software
16  * distributed under the License is distributed on an "AS IS" BASIS,
17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  * See the License for the specific language governing permissions and
19  * limitations under the License.
20  * 
21  * ============LICENSE_END=========================================================
22  */
23
24 package org.onap.appc.transactionrecorder.impl;
25
26 import com.sun.rowset.CachedRowSetImpl;
27 import org.junit.After;
28 import org.junit.Assert;
29 import org.junit.Before;
30 import org.junit.Test;
31 import org.mockito.Mockito;
32
33 import org.onap.appc.dao.util.dbcp.DBConnectionPool;
34 import org.onap.appc.dao.util.helper.DBHelper;
35 import org.onap.appc.domainmodel.lcm.Flags;
36 import org.onap.appc.domainmodel.lcm.RequestStatus;
37 import org.onap.appc.domainmodel.lcm.TransactionRecord;
38 import org.onap.appc.domainmodel.lcm.VNFOperation;
39 import org.onap.appc.exceptions.APPCException;
40 import org.onap.appc.transactionrecorder.objects.TransactionConstants;
41 import org.onap.ccsdk.sli.core.dblib.DbLibService;
42
43 import javax.sql.rowset.CachedRowSet;
44 import java.sql.*;
45 import java.time.Instant;
46 import java.time.ZoneOffset;
47 import java.time.format.DateTimeFormatter;
48 import java.util.ArrayList;
49 import java.util.HashMap;
50 import java.util.Map;
51 import java.util.UUID;
52
53 import static org.mockito.Matchers.*;
54
55 /**
56  * Test class for TransactionRecorder
57  */
58 public class TransactionRecorderImplTest {
59
60     private String dbUrl = "jdbc:h2:mem:test;MODE=MYSQL;DB_CLOSE_DELAY=-1";
61     private String username = "sa";
62     private String password = "sa";
63     private String driver = "org.h2.Driver";
64
65     private TransactionRecorderImpl transactionRecorderImpl;
66     private DbLibService dbLibService;
67
68     private DBConnectionPool dbConnectionPool;
69
70
71     /**
72      * Ideally JUnit should grab the SQL to create the transaction table from the same source used in deployments;
73      * however, at the time of writing this that was not possible.  Should it become possible in the future please
74      * update this JUnit test to use the deployment source.
75      * <p>
76      * Please ensure this table create script is identical to the source script used in a deployment.
77      */
78     private String TRANSACTION_CREATE_TABLE = "CREATE TABLE TRANSACTIONS (" +
79             "  TRANSACTION_ID VARCHAR(75) NOT NULL PRIMARY KEY," +
80             "  ORIGIN_TIMESTAMP DATETIME(3) NOT NULL," +
81             "  REQUEST_ID VARCHAR(256) NOT NULL," +
82             "  SUBREQUEST_ID VARCHAR(256) DEFAULT NULL," +
83             "  ORIGINATOR_ID VARCHAR(256) DEFAULT NULL," +
84             "  START_TIME DATETIME(3) NOT NULL," +
85             "  END_TIME DATETIME(3) DEFAULT NULL," +
86             "  TARGET_ID VARCHAR(256) NOT NULL," +
87             "  TARGET_TYPE VARCHAR(256) DEFAULT NULL," +
88             "  OPERATION VARCHAR(256) NOT NULL," +
89             "  RESULT_CODE INT(11) DEFAULT NULL," +
90             "  DESCRIPTION TEXT," +
91             "  STATE VARCHAR(50) NOT NULL," +
92             "  SERVICE_INSTANCE_ID VARCHAR(256) DEFAULT NULL," +
93             "  VNFC_NAME VARCHAR(256) DEFAULT NULL," +
94             "  VSERVER_ID VARCHAR(256) DEFAULT NULL," +
95             "  VF_MODULE_ID VARCHAR(256) DEFAULT NULL," +
96             "  MODE VARCHAR(50) NOT NULL," +
97             ")";
98     private String TRANSACTION_DROP_TABLE = "DROP TABLE IF EXISTS TRANSACTIONS";
99
100     @Before
101     public void setUp() throws Exception {
102         transactionRecorderImpl = new TransactionRecorderImpl();
103         transactionRecorderImpl.setAppcInstanceId("123");
104         dbLibService = Mockito.mock(DbLibService.class);
105         transactionRecorderImpl.setDbLibService(dbLibService);
106         dbConnectionPool = new DBConnectionPool(dbUrl, username, password, driver);
107         executeUpdate(TRANSACTION_CREATE_TABLE);
108
109     }
110
111
112     @After
113     public void shutdown() {
114         if (dbConnectionPool != null) {
115             executeUpdate(TRANSACTION_DROP_TABLE);
116             dbConnectionPool.shutdown();
117         }
118     }
119
120     private void executeUpdate(String updateSQL) {
121         Connection connection = null;
122         Statement stmt = null;
123         try {
124             connection = dbConnectionPool.getConnection();
125             stmt = connection.createStatement();
126             stmt.executeUpdate(updateSQL);
127         } catch (SQLException e) {
128             throw new RuntimeException(e);
129         } finally {
130             DBHelper.close(null, stmt, connection);
131         }
132     }
133
134     /**
135      * Verify the transactionRecorderImpl.sore() store the TransactionRecord correctly in the database.
136      */
137     @Test
138     public void testStore() throws Exception {
139
140         TransactionRecord input = prepareTransactionsInput();
141         Mockito.when(dbLibService.writeData(anyString(), anyObject(), anyString())).thenAnswer(invocation ->
142                 testStoreInMemory(invocation.getArguments()));
143         transactionRecorderImpl.store(input);
144
145     }
146
147     @Test
148     public void testGetInProgressRequests() throws SQLException, APPCException {
149         TransactionRecord record1 = prepareTransactionsInput();
150         insertRecord(record1);
151         TransactionRecord input = prepareTransactionsInput();
152         input.setStartTime(Instant.now());
153         Mockito.when(dbLibService.getData(anyString(), anyObject(), anyString())).thenAnswer(invocation ->
154                 inMemoryExecutionWithResultSet(invocation.getArguments()));
155         Assert.assertEquals(1, transactionRecorderImpl.getInProgressRequests(input).size());
156
157     }
158
159     @Test
160     public void testIsTransactionDuplicate() throws SQLException, APPCException {
161         TransactionRecord input = prepareTransactionsInput();
162         Mockito.when(dbLibService.getData(anyString(), anyObject(), anyString())).thenAnswer(invocation ->
163                 inMemoryExecutionWithResultSet(invocation.getArguments()));
164         Assert.assertFalse(transactionRecorderImpl.isTransactionDuplicate(input));
165
166     }
167
168     @Test
169     public void testGetInProgressRequestsCount() throws SQLException, APPCException {
170         TransactionRecord input = prepareTransactionsInput();
171         Mockito.when(dbLibService.getData(anyString(), anyObject(), anyString())).thenAnswer(invocation ->
172                 inMemoryExecutionWithResultSet(invocation.getArguments()));
173         Assert.assertEquals(0, transactionRecorderImpl.getInProgressRequestsCount().intValue());
174     }
175
176     @Test
177     public void testUpdate() throws APPCException, SQLException {
178         TransactionRecord input = prepareTransactionsInput();
179         insertRecord(input);
180         Map<TransactionConstants.TRANSACTION_ATTRIBUTES, String> updateColumns = new HashMap<>();
181         updateColumns.put(TransactionConstants.TRANSACTION_ATTRIBUTES.TARGET_TYPE, "Firewall");
182         Mockito.when(dbLibService.writeData(anyString(), anyObject(), anyString())).thenAnswer(invocation ->
183                 testUpdateInMemory(invocation.getArguments()));
184         transactionRecorderImpl.update(input.getTransactionId(), updateColumns);
185     }
186
187     @Test
188     public void testMarkTransactionsAborted() throws SQLException {
189         TransactionRecord input = prepareTransactionsInput();
190         insertRecord(input);
191         Mockito.when(dbLibService.writeData(anyString(), anyObject(), anyString())).thenAnswer(invocation ->
192                 testMarkAbortedInMemory(invocation.getArguments()));
193         transactionRecorderImpl.markTransactionsAborted("123~");
194     }
195
196     private ResultSet inMemoryExecutionWithResultSet(Object[] obj) throws Exception {
197         String query = (String) obj[0];
198         ArrayList<String> args = (ArrayList<String>) obj[1];
199         Connection con = dbConnectionPool.getConnection();
200         PreparedStatement ps = con.prepareStatement(query);
201         for (int i = 1; i <= args.size(); i++) {
202             ps.setString(i, args.get(i - 1));
203         }
204         CachedRowSet rowSet = new CachedRowSetImpl();
205         rowSet.populate(ps.executeQuery());
206         return rowSet;
207     }
208
209     private boolean testMarkAbortedInMemory(Object[] obj) throws Exception {
210         String query = (String) obj[0];
211         ArrayList<String> args = (ArrayList<String>) obj[1];
212         Connection con = dbConnectionPool.getConnection();
213         PreparedStatement ps = con.prepareStatement(query);
214         for (int i = 1; i <= args.size(); i++) {
215             ps.setString(i, args.get(i - 1));
216         }
217         ps.execute();
218         return isTransactionAborted();
219     }
220
221     private boolean isTransactionAborted() throws Exception {
222         String query = "SELECT COUNT(*) FROM  TRANSACTIONS WHERE STATE = ?";
223         Connection con = dbConnectionPool.getConnection();
224         PreparedStatement ps = con.prepareStatement(query);
225         ps.setString(1, RequestStatus.ABORTED.toString());
226         ResultSet rs = ps.executeQuery();
227         while (rs.next()) {
228             int value = rs.getInt(1);
229             if (value == 1) {
230                 System.out.println("Non terminal Transactions are aborted");
231                 return true;
232             }
233         }
234         throw new Exception("Transactions are not aborted");
235     }
236
237     private boolean testUpdateInMemory(Object[] obj) throws Exception {
238         String query = (String) obj[0];
239         ArrayList<String> args = (ArrayList<String>) obj[1];
240         Connection con = dbConnectionPool.getConnection();
241         PreparedStatement ps = con.prepareStatement(query);
242         for (int i = 1; i <= args.size(); i++) {
243             ps.setString(i, args.get(i - 1));
244         }
245         ps.execute();
246         String updatedValue = checkIfValueIsUpdated(args.get(1));
247         System.out.println("updated Value is " + updatedValue);
248         if (updatedValue.equals("Firewall")) {
249             return true;
250         }
251         throw new Exception("Not Updated");
252     }
253
254     private boolean testStoreInMemory(Object[] obj) throws Exception {
255         String query = (String) obj[0];
256         ArrayList<String> args = (ArrayList<String>) obj[1];
257         Connection con = dbConnectionPool.getConnection();
258         PreparedStatement ps = con.prepareStatement(query);
259         for (int i = 1; i <= args.size(); i++) {
260             ps.setString(i, args.get(i - 1));
261         }
262         ps.execute();
263         if (checkIfRowIsPresent(args.get(0))) {
264             return true;
265         }
266         throw new Exception("Failed to update");
267     }
268
269     private TransactionRecord prepareTransactionsInput() {
270         TransactionRecord input = new TransactionRecord();
271         input.setTransactionId(UUID.randomUUID().toString());
272         input.setOriginTimestamp(Instant.parse("2017-09-11T00:00:01.00Z"));
273         input.setRequestId("REQUEST_ID");
274         input.setSubRequestId("SUB_REQUEST_ID");
275         input.setOriginatorId("ORIGINATOR_ID");
276         input.setStartTime(Instant.parse("2017-09-11T00:00:02.00Z"));
277         input.setTargetId("TARGET_ID");
278         input.setTargetType("TARGET_TYPE");
279         input.setServiceInstanceId("SERVICE_INSTANCE_ID");
280         input.setOperation(VNFOperation.ActionStatus);
281         input.setResultCode(200);
282         input.setRequestState(RequestStatus.ACCEPTED);
283         input.setDescription("DESCRIPTION");
284         input.setMode(Flags.Mode.EXCLUSIVE);
285         return input;
286     }
287
288     private void insertRecord(TransactionRecord input) throws SQLException {
289         final String STORE_DATE_QUERY = TransactionConstants.INSERT_INTO + TransactionConstants.TRANSACTIONS +
290                 " values (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)";
291         Connection con = dbConnectionPool.getConnection();
292         PreparedStatement ps = con.prepareStatement(STORE_DATE_QUERY);
293         ArrayList<String> args = prepareArguments(input);
294         args.remove(0);
295         args.add(0, "123~" + input.getTransactionId());
296         for (int i = 1; i <= 18; i++) {
297             ps.setString(i, args.get(i - 1));
298         }
299         ps.execute();
300         if (checkIfRowIsPresent(args.get(0))) {
301             System.out.println("RECORD INSERTED " + args.get(0));
302         }
303
304     }
305
306     private ArrayList<String> prepareArguments(TransactionRecord input) {
307         ArrayList<String> arguments = new ArrayList<>();
308         arguments.add(input.getTransactionId());
309         arguments.add(dateToStringConverterMillis(input.getOriginTimestamp()));
310         arguments.add(input.getRequestId());
311         arguments.add(input.getSubRequestId());
312         arguments.add(input.getOriginatorId());
313         arguments.add(dateToStringConverterMillis(input.getStartTime()));
314         arguments.add(dateToStringConverterMillis(input.getEndTime()));
315         arguments.add(input.getTargetId());
316         arguments.add(input.getTargetType());
317         arguments.add(input.getOperation().name());
318         arguments.add(String.valueOf(input.getResultCode()));
319         arguments.add(input.getDescription());
320         arguments.add(input.getRequestState());
321         arguments.add(input.getServiceInstanceId());
322         arguments.add(input.getVnfcName());
323         arguments.add(input.getVserverId());
324         arguments.add(input.getVfModuleId());
325         arguments.add(input.getMode());
326
327         return arguments;
328     }
329
330     private static String dateToStringConverterMillis(Instant date) {
331         if (date == null) {
332             return null;
333         }
334         DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS").withZone(ZoneOffset.UTC);
335         return formatter.format(date);
336     }
337
338     private boolean checkIfRowIsPresent(String key) {
339         Connection con = null;
340         ResultSet rs = null;
341         PreparedStatement ps = null;
342         try {
343             con = dbConnectionPool.getConnection();
344             ps = con.prepareStatement("SELECT COUNT(*) FROM  TRANSACTIONS WHERE TRANSACTION_ID = ?");
345             ps.setString(1, key);
346             rs = ps.executeQuery();
347             while (rs.next()) {
348                 int value = rs.getInt(1);
349                 System.out.println("KEY checked is " + key + " COUNT RETURNED IS " + value);
350                 if (value == 1) {
351                     return true;
352                 }
353             }
354         } catch (SQLException e) {
355             e.printStackTrace();
356         } finally {
357             DBHelper.close(rs, ps, con);
358         }
359         return false;
360     }
361
362     private String checkIfValueIsUpdated(String key) throws Exception {
363         Connection con = dbConnectionPool.getConnection();
364         PreparedStatement ps = con.prepareStatement("SELECT TARGET_TYPE FROM  TRANSACTIONS WHERE TRANSACTION_ID = ?");
365         ps.setString(1, key);
366         ResultSet rs = ps.executeQuery();
367         while (rs.next()) {
368             String value = rs.getString("TARGET_TYPE");
369             return value;
370         }
371         throw new Exception("Value not found");
372     }
373
374
375     /**
376      * Verify the transactionRecorderImpl. getRecords () can be fetch with each of the parameter combinations
377      * @throws Exception
378      *//*
379     @Test
380     public void test_api_getRecords() throws Exception {
381
382
383         final int requestId = 0;
384         final int subrequestId = 1;
385         final int originatorId = 2;
386         final int vnfId = 3;
387         final int requestStatus = 4;
388
389
390         String[][] trCreateMatrix = {
391             {"request1", "subrequestId1", "originatorId1", "vnfId1", RequestStatus.UNKNOWN.name()},
392             {"request1", "subrequestId2", "originatorId1", "vnfId1", RequestStatus.RECEIVED.name()},
393             {"request2", "subrequestId1", "originatorId1", "vnfId1", RequestStatus.ACCEPTED.name()},
394             {"request2", "subrequestId2", "originatorId1", "vnfId1", RequestStatus.REJECTED.name()},
395             {"request1", "subrequestId1", "originatorId1", "vnfId2", RequestStatus.SUCCESSFUL.name()},
396             {"request1", "subrequestId2", "originatorId1", "vnfId2", RequestStatus.FAILED.name()},
397             {"request2", "subrequestId1", "originatorId1", "vnfId2", RequestStatus.TIMEOUT.name()},
398             {"request2", "subrequestId2", "originatorId1", "vnfId2", RequestStatus.ABORTED.name()},
399             {"request1", "subrequestId1", "originatorId2", "vnfId1", RequestStatus.UNKNOWN.name()},
400             {"request1", "subrequestId2", "originatorId2", "vnfId1", RequestStatus.RECEIVED.name()},
401             {"request2", "subrequestId1", "originatorId2", "vnfId1", RequestStatus.ACCEPTED.name()},
402             {"request2", "subrequestId2", "originatorId2", "vnfId1", RequestStatus.REJECTED.name()},
403             {"request1", "subrequestId1", "originatorId2", "vnfId2", RequestStatus.SUCCESSFUL.name()},
404             {"request1", "subrequestId2", "originatorId2", "vnfId2", RequestStatus.FAILED.name()},
405             {"request2", "subrequestId1", "originatorId2", "vnfId2", RequestStatus.TIMEOUT.name()},
406             {"request2", "subrequestId2", "originatorId2", "vnfId2", RequestStatus.ABORTED.name()},
407         };
408
409
410         TransactionRecord tr = new TransactionRecord();
411         tr.setTimeStamp(Instant.parse("2017-09-11T00:00:01.00Z"));
412         tr.setStartTime(Instant.parse("2017-09-11T00:00:02.00Z"));
413         tr.setEndTime(Instant.parse("2017-09-11T00:00:03.00Z"));
414         tr.setTargetType("TARGET_TYPE");
415         tr.setSubComponent("SUB_COMPONENT");
416         tr.setOperation(VNFOperation.ActionStatus);
417         tr.setResultCode("RESULT_CODE");
418         tr.setDescription("DESCRIPTION");
419
420         for (int row = 0; row < trCreateMatrix.length; row++) {
421             tr.setRequestID(trCreateMatrix[row][requestId]);
422             tr.setSubRequestID(trCreateMatrix[row][subrequestId]);
423             tr.setOriginatorId(trCreateMatrix[row][originatorId]);
424             tr.setTargetID(trCreateMatrix[row][vnfId]);
425             tr.setRequestStatus(RequestStatus.valueOf(trCreateMatrix[row][requestStatus]));
426             transactionRecorderImpl.store(tr);
427         }
428
429
430         String[][] trSearchMatrix = {
431             {"request1", null, null, "vnfId1"},
432             {"request2", "subrequestId1", null, "vnfId1"},
433             {"request1", null, "originatorId1", "vnfId1"},
434             {"request2", "subrequestId2", "originatorId1", "vnfId1"},
435         };
436
437
438         for (int i = 0; i < trSearchMatrix.length; i++) {
439             final int row = i;
440             List<RequestStatus> actualList = transactionRecorderImpl
441                 .getRecords(trSearchMatrix[row][requestId], trSearchMatrix[row][subrequestId],
442                     trSearchMatrix[row][originatorId], trSearchMatrix[row][vnfId])
443                 .stream()
444                 .sorted()
445                 .collect(Collectors.toList());
446
447             List<RequestStatus> expectedList = Arrays.stream(trCreateMatrix)
448                 .filter(entry -> entry[requestId].equals(trSearchMatrix[row][requestId]))
449                 .filter(entry -> trSearchMatrix[row][subrequestId] == null || entry[subrequestId].equals
450                     (trSearchMatrix[row][subrequestId]))
451                 .filter(entry -> trSearchMatrix[row][originatorId] == null || entry[originatorId].equals
452                     (trSearchMatrix[row][originatorId]))
453                 .filter(entry -> entry[vnfId].equals(trSearchMatrix[row][vnfId]))
454                 .map(entry -> RequestStatus.valueOf(entry[requestStatus]))
455                 .sorted()
456                 .collect(Collectors.toList());
457             System.out.println(expectedList);
458             System.out.println(actualList);
459             Assert.assertEquals("Unexpected results: ", expectedList, actualList);
460
461         }
462
463
464     }*/
465 }