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