2 * ============LICENSE_START=======================================================
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
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 * ECOMP is a trademark and service mark of AT&T Intellectual Property.
22 * ============LICENSE_END=========================================================
25 package org.onap.appc.transactionrecorder.impl;
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;
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;
44 import javax.sql.rowset.CachedRowSet;
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;
52 import java.util.UUID;
54 import static org.mockito.Matchers.*;
57 * Test class for TransactionRecorder
59 public class TransactionRecorderImplTest {
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";
66 private TransactionRecorderImpl transactionRecorderImpl;
67 private DbLibService dbLibService;
69 private DBConnectionPool dbConnectionPool;
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.
77 * Please ensure this table create script is identical to the source script used in a deployment.
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," +
99 private String TRANSACTION_DROP_TABLE = "DROP TABLE IF EXISTS TRANSACTIONS";
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);
114 public void shutdown() {
115 if (dbConnectionPool != null) {
116 executeUpdate(TRANSACTION_DROP_TABLE);
117 dbConnectionPool.shutdown();
121 private void executeUpdate(String updateSQL) {
122 Connection connection = null;
123 Statement stmt = null;
125 connection = dbConnectionPool.getConnection();
126 stmt = connection.createStatement();
127 stmt.executeUpdate(updateSQL);
128 } catch (SQLException e) {
129 throw new RuntimeException(e);
131 DBHelper.close(null, stmt, connection);
136 * Verify the transactionRecorderImpl.sore() store the TransactionRecord correctly in the database.
139 public void testStore() throws Exception {
141 TransactionRecord input = prepareTransactionsInput();
142 Mockito.when(dbLibService.writeData(anyString(), anyObject(), anyString())).thenAnswer(invocation ->
143 testStoreInMemory(invocation.getArguments()));
144 transactionRecorderImpl.store(input);
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());
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));
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());
178 public void testUpdate() throws APPCException, SQLException {
179 TransactionRecord input = prepareTransactionsInput();
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);
189 public void testMarkTransactionsAborted() throws SQLException {
190 TransactionRecord input = prepareTransactionsInput();
192 Mockito.when(dbLibService.writeData(anyString(), anyObject(), anyString())).thenAnswer(invocation ->
193 testMarkAbortedInMemory(invocation.getArguments()));
194 transactionRecorderImpl.markTransactionsAborted("123~");
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));
205 CachedRowSet rowSet = new CachedRowSetImpl();
206 rowSet.populate(ps.executeQuery());
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));
219 return isTransactionAborted();
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();
229 int value = rs.getInt(1);
231 System.out.println("Non terminal Transactions are aborted");
235 throw new Exception("Transactions are not aborted");
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));
247 String updatedValue = checkIfValueIsUpdated(args.get(1));
248 System.out.println("updated Value is " + updatedValue);
249 if (updatedValue.equals("Firewall")) {
252 throw new Exception("Not Updated");
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));
264 if (checkIfRowIsPresent(args.get(0))) {
267 throw new Exception("Failed to update");
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);
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);
296 args.add(0, "123~" + input.getTransactionId());
297 for (int i = 1; i <= 18; i++) {
298 ps.setString(i, args.get(i - 1));
301 if (checkIfRowIsPresent(args.get(0))) {
302 System.out.println("RECORD INSERTED " + args.get(0));
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());
331 private static String dateToStringConverterMillis(Instant date) {
335 DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS").withZone(ZoneOffset.UTC);
336 return formatter.format(date);
339 private boolean checkIfRowIsPresent(String key) {
340 Connection con = null;
342 PreparedStatement ps = null;
344 con = dbConnectionPool.getConnection();
345 ps = con.prepareStatement("SELECT COUNT(*) FROM TRANSACTIONS WHERE TRANSACTION_ID = ?");
346 ps.setString(1, key);
347 rs = ps.executeQuery();
349 int value = rs.getInt(1);
350 System.out.println("KEY checked is " + key + " COUNT RETURNED IS " + value);
355 } catch (SQLException e) {
358 DBHelper.close(rs, ps, con);
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();
369 String value = rs.getString("TARGET_TYPE");
372 throw new Exception("Value not found");
377 * Verify the transactionRecorderImpl. getRecords () can be fetch with each of the parameter combinations
381 public void test_api_getRecords() throws Exception {
384 final int requestId = 0;
385 final int subrequestId = 1;
386 final int originatorId = 2;
388 final int requestStatus = 4;
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()},
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");
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);
431 String[][] trSearchMatrix = {
432 {"request1", null, null, "vnfId1"},
433 {"request2", "subrequestId1", null, "vnfId1"},
434 {"request1", null, "originatorId1", "vnfId1"},
435 {"request2", "subrequestId2", "originatorId1", "vnfId1"},
439 for (int i = 0; i < trSearchMatrix.length; i++) {
441 List<RequestStatus> actualList = transactionRecorderImpl
442 .getRecords(trSearchMatrix[row][requestId], trSearchMatrix[row][subrequestId],
443 trSearchMatrix[row][originatorId], trSearchMatrix[row][vnfId])
446 .collect(Collectors.toList());
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]))
457 .collect(Collectors.toList());
458 System.out.println(expectedList);
459 System.out.println(actualList);
460 Assert.assertEquals("Unexpected results: ", expectedList, actualList);