2 * ============LICENSE_START=======================================================
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
13 * http://www.apache.org/licenses/LICENSE-2.0
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.
21 * ============LICENSE_END=========================================================
24 package org.onap.appc.transactionrecorder.impl;
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;
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;
43 import javax.sql.rowset.CachedRowSet;
45 import java.time.Instant;
46 import java.time.ZoneOffset;
47 import java.time.format.DateTimeFormatter;
48 import java.time.temporal.ChronoUnit;
49 import java.util.ArrayList;
50 import java.util.HashMap;
51 import java.util.List;
53 import java.util.UUID;
55 import static org.mockito.Matchers.*;
58 * Test class for TransactionRecorder
60 public class TransactionRecorderImplTest {
62 private String dbUrl = "jdbc:h2:mem:test;MODE=MYSQL;DB_CLOSE_DELAY=-1";
63 private String username = "sa";
64 private String password = "sa";
65 private String driver = "org.h2.Driver";
67 private TransactionRecorderImpl transactionRecorderImpl;
68 private DbLibService dbLibService;
70 private DBConnectionPool dbConnectionPool;
74 * Ideally JUnit should grab the SQL to create the transaction table from the same source used in deployments;
75 * however, at the time of writing this that was not possible. Should it become possible in the future please
76 * update this JUnit test to use the deployment source.
78 * Please ensure this table create script is identical to the source script used in a deployment.
80 private String TRANSACTION_CREATE_TABLE = "CREATE TABLE TRANSACTIONS (" +
81 " TRANSACTION_ID VARCHAR(75) NOT NULL PRIMARY KEY," +
82 " ORIGIN_TIMESTAMP DATETIME(3) NOT NULL," +
83 " REQUEST_ID VARCHAR(256) NOT NULL," +
84 " SUBREQUEST_ID VARCHAR(256) DEFAULT NULL," +
85 " ORIGINATOR_ID VARCHAR(256) DEFAULT NULL," +
86 " START_TIME DATETIME(3) NOT NULL," +
87 " END_TIME DATETIME(3) DEFAULT NULL," +
88 " TARGET_ID VARCHAR(256) NOT NULL," +
89 " TARGET_TYPE VARCHAR(256) DEFAULT NULL," +
90 " OPERATION VARCHAR(256) NOT NULL," +
91 " RESULT_CODE INT(11) DEFAULT NULL," +
92 " DESCRIPTION TEXT," +
93 " STATE VARCHAR(50) NOT NULL," +
94 " SERVICE_INSTANCE_ID VARCHAR(256) DEFAULT NULL," +
95 " VNFC_NAME VARCHAR(256) DEFAULT NULL," +
96 " VSERVER_ID VARCHAR(256) DEFAULT NULL," +
97 " VF_MODULE_ID VARCHAR(256) DEFAULT NULL," +
98 " MODE VARCHAR(50) NOT NULL," +
100 private String TRANSACTION_DROP_TABLE = "DROP TABLE IF EXISTS TRANSACTIONS";
103 public void setUp() throws Exception {
104 transactionRecorderImpl = new TransactionRecorderImpl();
105 transactionRecorderImpl.setAppcInstanceId("123");
106 dbLibService = Mockito.mock(DbLibService.class);
107 transactionRecorderImpl.setDbLibService(dbLibService);
108 dbConnectionPool = new DBConnectionPool(dbUrl, username, password, driver);
109 executeUpdate(TRANSACTION_CREATE_TABLE);
115 public void shutdown() {
116 if (dbConnectionPool != null) {
117 executeUpdate(TRANSACTION_DROP_TABLE);
118 dbConnectionPool.shutdown();
122 private void executeUpdate(String updateSQL) {
123 Connection connection = null;
124 Statement stmt = null;
126 connection = dbConnectionPool.getConnection();
127 stmt = connection.createStatement();
128 stmt.executeUpdate(updateSQL);
129 } catch (SQLException e) {
130 throw new RuntimeException(e);
132 DBHelper.close(null, stmt, connection);
137 * Verify the transactionRecorderImpl.sore() store the TransactionRecord correctly in the database.
140 public void testStore() throws Exception {
142 TransactionRecord input = prepareTransactionsInput();
143 Mockito.when(dbLibService.writeData(anyString(), anyObject(), anyString())).thenAnswer(invocation ->
144 testStoreInMemory(invocation.getArguments()));
145 transactionRecorderImpl.store(input);
150 public void testGetInProgressRequests() throws SQLException, APPCException {
151 TransactionRecord record1 = prepareTransactionsInput();
152 insertRecord(record1);
153 TransactionRecord input = prepareTransactionsInput();
154 input.setStartTime(Instant.now());
155 Mockito.when(dbLibService.getData(anyString(), anyObject(), anyString())).thenAnswer(invocation ->
156 inMemoryExecutionWithResultSet(invocation.getArguments()));
157 Assert.assertEquals(1, transactionRecorderImpl.getInProgressRequests(input,0).size());
162 public void testGetInProgressRequestsWithinTimeInterval() throws SQLException, APPCException {
163 TransactionRecord record1 = prepareTransactionsInput();
164 record1.setStartTime(Instant.now().minus(4,ChronoUnit.HOURS));
165 insertRecord(record1);
166 TransactionRecord input = prepareTransactionsInput();
167 input.setStartTime(Instant.now());
168 Mockito.when(dbLibService.getData(anyString(), anyObject(), anyString())).thenAnswer(invocation ->
169 inMemoryExecutionWithResultSet(invocation.getArguments()));
170 List<TransactionRecord> aList= transactionRecorderImpl.getInProgressRequests(input,12);
171 Assert.assertEquals(1, transactionRecorderImpl.getInProgressRequests(input,12).size());
176 public void testIsTransactionDuplicate() throws SQLException, APPCException {
177 TransactionRecord input = prepareTransactionsInput();
178 Mockito.when(dbLibService.getData(anyString(), anyObject(), anyString())).thenAnswer(invocation ->
179 inMemoryExecutionWithResultSet(invocation.getArguments()));
180 Assert.assertFalse(transactionRecorderImpl.isTransactionDuplicate(input));
185 public void testGetInProgressRequestsCount() throws SQLException, APPCException {
186 TransactionRecord input = prepareTransactionsInput();
187 Mockito.when(dbLibService.getData(anyString(), anyObject(), anyString())).thenAnswer(invocation ->
188 inMemoryExecutionWithResultSet(invocation.getArguments()));
189 Assert.assertEquals(0, transactionRecorderImpl.getInProgressRequestsCount().intValue());
193 public void testUpdate() throws APPCException, SQLException {
194 TransactionRecord input = prepareTransactionsInput();
196 Map<TransactionConstants.TRANSACTION_ATTRIBUTES, String> updateColumns = new HashMap<>();
197 updateColumns.put(TransactionConstants.TRANSACTION_ATTRIBUTES.TARGET_TYPE, "Firewall");
198 Mockito.when(dbLibService.writeData(anyString(), anyObject(), anyString())).thenAnswer(invocation ->
199 testUpdateInMemory(invocation.getArguments()));
200 transactionRecorderImpl.update(input.getTransactionId(), updateColumns);
204 public void testMarkTransactionsAborted() throws SQLException {
205 TransactionRecord input = prepareTransactionsInput();
207 Mockito.when(dbLibService.writeData(anyString(), anyObject(), anyString())).thenAnswer(invocation ->
208 testMarkAbortedInMemory(invocation.getArguments()));
209 transactionRecorderImpl.markTransactionsAborted("123~");
212 private ResultSet inMemoryExecutionWithResultSet(Object[] obj) throws Exception {
213 String query = (String) obj[0];
214 ArrayList<String> args = (ArrayList<String>) obj[1];
215 Connection con = dbConnectionPool.getConnection();
216 PreparedStatement ps = con.prepareStatement(query);
217 for (int i = 1; i <= args.size(); i++) {
218 ps.setString(i, args.get(i - 1));
220 CachedRowSet rowSet = new CachedRowSetImpl();
221 rowSet.populate(ps.executeQuery());
225 private boolean testMarkAbortedInMemory(Object[] obj) throws Exception {
226 String query = (String) obj[0];
227 ArrayList<String> args = (ArrayList<String>) obj[1];
228 Connection con = dbConnectionPool.getConnection();
229 PreparedStatement ps = con.prepareStatement(query);
230 for (int i = 1; i <= args.size(); i++) {
231 ps.setString(i, args.get(i - 1));
234 return isTransactionAborted();
237 private boolean isTransactionAborted() throws Exception {
238 String query = "SELECT COUNT(*) FROM TRANSACTIONS WHERE STATE = ?";
239 Connection con = dbConnectionPool.getConnection();
240 PreparedStatement ps = con.prepareStatement(query);
241 ps.setString(1, RequestStatus.ABORTED.toString());
242 ResultSet rs = ps.executeQuery();
244 int value = rs.getInt(1);
246 System.out.println("Non terminal Transactions are aborted");
250 throw new Exception("Transactions are not aborted");
253 private boolean testUpdateInMemory(Object[] obj) throws Exception {
254 String query = (String) obj[0];
255 ArrayList<String> args = (ArrayList<String>) obj[1];
256 Connection con = dbConnectionPool.getConnection();
257 PreparedStatement ps = con.prepareStatement(query);
258 for (int i = 1; i <= args.size(); i++) {
259 ps.setString(i, args.get(i - 1));
262 String updatedValue = checkIfValueIsUpdated(args.get(1));
263 System.out.println("updated Value is " + updatedValue);
264 if (updatedValue.equals("Firewall")) {
267 throw new Exception("Not Updated");
270 private boolean testStoreInMemory(Object[] obj) throws Exception {
271 String query = (String) obj[0];
272 ArrayList<String> args = (ArrayList<String>) obj[1];
273 Connection con = dbConnectionPool.getConnection();
274 PreparedStatement ps = con.prepareStatement(query);
275 for (int i = 1; i <= args.size(); i++) {
276 ps.setString(i, args.get(i - 1));
279 if (checkIfRowIsPresent(args.get(0))) {
282 throw new Exception("Failed to update");
285 private TransactionRecord prepareTransactionsInput() {
286 TransactionRecord input = new TransactionRecord();
287 input.setTransactionId(UUID.randomUUID().toString());
288 input.setOriginTimestamp(Instant.parse("2017-09-11T00:00:01.00Z"));
289 input.setRequestId("REQUEST_ID");
290 input.setSubRequestId("SUB_REQUEST_ID");
291 input.setOriginatorId("ORIGINATOR_ID");
292 input.setStartTime(Instant.parse("2017-09-11T00:00:02.00Z"));
293 input.setTargetId("TARGET_ID");
294 input.setTargetType("TARGET_TYPE");
295 input.setServiceInstanceId("SERVICE_INSTANCE_ID");
296 input.setOperation(VNFOperation.ActionStatus);
297 input.setResultCode(200);
298 input.setRequestState(RequestStatus.ACCEPTED);
299 input.setDescription("DESCRIPTION");
300 input.setMode(Flags.Mode.EXCLUSIVE);
304 private void insertRecord(TransactionRecord input) throws SQLException {
305 final String STORE_DATE_QUERY = TransactionConstants.INSERT_INTO + TransactionConstants.TRANSACTIONS +
306 " values (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)";
307 Connection con = dbConnectionPool.getConnection();
308 PreparedStatement ps = con.prepareStatement(STORE_DATE_QUERY);
309 ArrayList<String> args = prepareArguments(input);
311 args.add(0, "123~" + input.getTransactionId());
312 for (int i = 1; i <= 18; i++) {
313 ps.setString(i, args.get(i - 1));
316 if (checkIfRowIsPresent(args.get(0))) {
317 System.out.println("RECORD INSERTED " + args.get(0));
322 private ArrayList<String> prepareArguments(TransactionRecord input) {
323 ArrayList<String> arguments = new ArrayList<>();
324 arguments.add(input.getTransactionId());
325 arguments.add(dateToStringConverterMillis(input.getOriginTimestamp()));
326 arguments.add(input.getRequestId());
327 arguments.add(input.getSubRequestId());
328 arguments.add(input.getOriginatorId());
329 arguments.add(dateToStringConverterMillis(input.getStartTime()));
330 arguments.add(dateToStringConverterMillis(input.getEndTime()));
331 arguments.add(input.getTargetId());
332 arguments.add(input.getTargetType());
333 arguments.add(input.getOperation().name());
334 arguments.add(String.valueOf(input.getResultCode()));
335 arguments.add(input.getDescription());
336 arguments.add(input.getRequestState());
337 arguments.add(input.getServiceInstanceId());
338 arguments.add(input.getVnfcName());
339 arguments.add(input.getVserverId());
340 arguments.add(input.getVfModuleId());
341 arguments.add(input.getMode());
346 private static String dateToStringConverterMillis(Instant date) {
350 DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS").withZone(ZoneOffset.UTC);
351 return formatter.format(date);
354 private boolean checkIfRowIsPresent(String key) {
355 Connection con = null;
357 PreparedStatement ps = null;
359 con = dbConnectionPool.getConnection();
360 ps = con.prepareStatement("SELECT COUNT(*) FROM TRANSACTIONS WHERE TRANSACTION_ID = ?");
361 ps.setString(1, key);
362 rs = ps.executeQuery();
364 int value = rs.getInt(1);
365 System.out.println("KEY checked is " + key + " COUNT RETURNED IS " + value);
370 } catch (SQLException e) {
373 DBHelper.close(rs, ps, con);
378 private String checkIfValueIsUpdated(String key) throws Exception {
379 Connection con = dbConnectionPool.getConnection();
380 PreparedStatement ps = con.prepareStatement("SELECT TARGET_TYPE FROM TRANSACTIONS WHERE TRANSACTION_ID = ?");
381 ps.setString(1, key);
382 ResultSet rs = ps.executeQuery();
384 String value = rs.getString("TARGET_TYPE");
387 throw new Exception("Value not found");
392 * Verify the transactionRecorderImpl. getRecords () can be fetch with each of the parameter combinations
396 public void test_api_getRecords() throws Exception {
399 final int requestId = 0;
400 final int subrequestId = 1;
401 final int originatorId = 2;
403 final int requestStatus = 4;
406 String[][] trCreateMatrix = {
407 {"request1", "subrequestId1", "originatorId1", "vnfId1", RequestStatus.UNKNOWN.name()},
408 {"request1", "subrequestId2", "originatorId1", "vnfId1", RequestStatus.RECEIVED.name()},
409 {"request2", "subrequestId1", "originatorId1", "vnfId1", RequestStatus.ACCEPTED.name()},
410 {"request2", "subrequestId2", "originatorId1", "vnfId1", RequestStatus.REJECTED.name()},
411 {"request1", "subrequestId1", "originatorId1", "vnfId2", RequestStatus.SUCCESSFUL.name()},
412 {"request1", "subrequestId2", "originatorId1", "vnfId2", RequestStatus.FAILED.name()},
413 {"request2", "subrequestId1", "originatorId1", "vnfId2", RequestStatus.TIMEOUT.name()},
414 {"request2", "subrequestId2", "originatorId1", "vnfId2", RequestStatus.ABORTED.name()},
415 {"request1", "subrequestId1", "originatorId2", "vnfId1", RequestStatus.UNKNOWN.name()},
416 {"request1", "subrequestId2", "originatorId2", "vnfId1", RequestStatus.RECEIVED.name()},
417 {"request2", "subrequestId1", "originatorId2", "vnfId1", RequestStatus.ACCEPTED.name()},
418 {"request2", "subrequestId2", "originatorId2", "vnfId1", RequestStatus.REJECTED.name()},
419 {"request1", "subrequestId1", "originatorId2", "vnfId2", RequestStatus.SUCCESSFUL.name()},
420 {"request1", "subrequestId2", "originatorId2", "vnfId2", RequestStatus.FAILED.name()},
421 {"request2", "subrequestId1", "originatorId2", "vnfId2", RequestStatus.TIMEOUT.name()},
422 {"request2", "subrequestId2", "originatorId2", "vnfId2", RequestStatus.ABORTED.name()},
426 TransactionRecord tr = new TransactionRecord();
427 tr.setTimeStamp(Instant.parse("2017-09-11T00:00:01.00Z"));
428 tr.setStartTime(Instant.parse("2017-09-11T00:00:02.00Z"));
429 tr.setEndTime(Instant.parse("2017-09-11T00:00:03.00Z"));
430 tr.setTargetType("TARGET_TYPE");
431 tr.setSubComponent("SUB_COMPONENT");
432 tr.setOperation(VNFOperation.ActionStatus);
433 tr.setResultCode("RESULT_CODE");
434 tr.setDescription("DESCRIPTION");
436 for (int row = 0; row < trCreateMatrix.length; row++) {
437 tr.setRequestID(trCreateMatrix[row][requestId]);
438 tr.setSubRequestID(trCreateMatrix[row][subrequestId]);
439 tr.setOriginatorId(trCreateMatrix[row][originatorId]);
440 tr.setTargetID(trCreateMatrix[row][vnfId]);
441 tr.setRequestStatus(RequestStatus.valueOf(trCreateMatrix[row][requestStatus]));
442 transactionRecorderImpl.store(tr);
446 String[][] trSearchMatrix = {
447 {"request1", null, null, "vnfId1"},
448 {"request2", "subrequestId1", null, "vnfId1"},
449 {"request1", null, "originatorId1", "vnfId1"},
450 {"request2", "subrequestId2", "originatorId1", "vnfId1"},
454 for (int i = 0; i < trSearchMatrix.length; i++) {
456 List<RequestStatus> actualList = transactionRecorderImpl
457 .getRecords(trSearchMatrix[row][requestId], trSearchMatrix[row][subrequestId],
458 trSearchMatrix[row][originatorId], trSearchMatrix[row][vnfId])
461 .collect(Collectors.toList());
463 List<RequestStatus> expectedList = Arrays.stream(trCreateMatrix)
464 .filter(entry -> entry[requestId].equals(trSearchMatrix[row][requestId]))
465 .filter(entry -> trSearchMatrix[row][subrequestId] == null || entry[subrequestId].equals
466 (trSearchMatrix[row][subrequestId]))
467 .filter(entry -> trSearchMatrix[row][originatorId] == null || entry[originatorId].equals
468 (trSearchMatrix[row][originatorId]))
469 .filter(entry -> entry[vnfId].equals(trSearchMatrix[row][vnfId]))
470 .map(entry -> RequestStatus.valueOf(entry[requestStatus]))
472 .collect(Collectors.toList());
473 System.out.println(expectedList);
474 System.out.println(actualList);
475 Assert.assertEquals("Unexpected results: ", expectedList, actualList);