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.util.ArrayList;
49 import java.util.HashMap;
51 import java.util.UUID;
53 import static org.mockito.Matchers.*;
56 * Test class for TransactionRecorder
58 public class TransactionRecorderImplTest {
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";
65 private TransactionRecorderImpl transactionRecorderImpl;
66 private DbLibService dbLibService;
68 private DBConnectionPool dbConnectionPool;
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.
76 * Please ensure this table create script is identical to the source script used in a deployment.
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," +
98 private String TRANSACTION_DROP_TABLE = "DROP TABLE IF EXISTS TRANSACTIONS";
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);
113 public void shutdown() {
114 if (dbConnectionPool != null) {
115 executeUpdate(TRANSACTION_DROP_TABLE);
116 dbConnectionPool.shutdown();
120 private void executeUpdate(String updateSQL) {
121 Connection connection = null;
122 Statement stmt = null;
124 connection = dbConnectionPool.getConnection();
125 stmt = connection.createStatement();
126 stmt.executeUpdate(updateSQL);
127 } catch (SQLException e) {
128 throw new RuntimeException(e);
130 DBHelper.close(null, stmt, connection);
135 * Verify the transactionRecorderImpl.sore() store the TransactionRecord correctly in the database.
138 public void testStore() throws Exception {
140 TransactionRecord input = prepareTransactionsInput();
141 Mockito.when(dbLibService.writeData(anyString(), anyObject(), anyString())).thenAnswer(invocation ->
142 testStoreInMemory(invocation.getArguments()));
143 transactionRecorderImpl.store(input);
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());
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));
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());
177 public void testUpdate() throws APPCException, SQLException {
178 TransactionRecord input = prepareTransactionsInput();
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);
188 public void testMarkTransactionsAborted() throws SQLException {
189 TransactionRecord input = prepareTransactionsInput();
191 Mockito.when(dbLibService.writeData(anyString(), anyObject(), anyString())).thenAnswer(invocation ->
192 testMarkAbortedInMemory(invocation.getArguments()));
193 transactionRecorderImpl.markTransactionsAborted("123~");
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));
204 CachedRowSet rowSet = new CachedRowSetImpl();
205 rowSet.populate(ps.executeQuery());
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));
218 return isTransactionAborted();
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();
228 int value = rs.getInt(1);
230 System.out.println("Non terminal Transactions are aborted");
234 throw new Exception("Transactions are not aborted");
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));
246 String updatedValue = checkIfValueIsUpdated(args.get(1));
247 System.out.println("updated Value is " + updatedValue);
248 if (updatedValue.equals("Firewall")) {
251 throw new Exception("Not Updated");
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));
263 if (checkIfRowIsPresent(args.get(0))) {
266 throw new Exception("Failed to update");
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);
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);
295 args.add(0, "123~" + input.getTransactionId());
296 for (int i = 1; i <= 18; i++) {
297 ps.setString(i, args.get(i - 1));
300 if (checkIfRowIsPresent(args.get(0))) {
301 System.out.println("RECORD INSERTED " + args.get(0));
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());
330 private static String dateToStringConverterMillis(Instant date) {
334 DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS").withZone(ZoneOffset.UTC);
335 return formatter.format(date);
338 private boolean checkIfRowIsPresent(String key) {
339 Connection con = null;
341 PreparedStatement ps = null;
343 con = dbConnectionPool.getConnection();
344 ps = con.prepareStatement("SELECT COUNT(*) FROM TRANSACTIONS WHERE TRANSACTION_ID = ?");
345 ps.setString(1, key);
346 rs = ps.executeQuery();
348 int value = rs.getInt(1);
349 System.out.println("KEY checked is " + key + " COUNT RETURNED IS " + value);
354 } catch (SQLException e) {
357 DBHelper.close(rs, ps, con);
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();
368 String value = rs.getString("TARGET_TYPE");
371 throw new Exception("Value not found");
376 * Verify the transactionRecorderImpl. getRecords () can be fetch with each of the parameter combinations
380 public void test_api_getRecords() throws Exception {
383 final int requestId = 0;
384 final int subrequestId = 1;
385 final int originatorId = 2;
387 final int requestStatus = 4;
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()},
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");
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);
430 String[][] trSearchMatrix = {
431 {"request1", null, null, "vnfId1"},
432 {"request2", "subrequestId1", null, "vnfId1"},
433 {"request1", null, "originatorId1", "vnfId1"},
434 {"request2", "subrequestId2", "originatorId1", "vnfId1"},
438 for (int i = 0; i < trSearchMatrix.length; i++) {
440 List<RequestStatus> actualList = transactionRecorderImpl
441 .getRecords(trSearchMatrix[row][requestId], trSearchMatrix[row][subrequestId],
442 trSearchMatrix[row][originatorId], trSearchMatrix[row][vnfId])
445 .collect(Collectors.toList());
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]))
456 .collect(Collectors.toList());
457 System.out.println(expectedList);
458 System.out.println(actualList);
459 Assert.assertEquals("Unexpected results: ", expectedList, actualList);