/* * Licensed under the Apache License, Version 2.0 (the "License"); * * You may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * limitations under the License. * * Contributions from 2013-2017 where performed either by US government * employees, or under US Veterans Health Administration contracts. * * US Veterans Health Administration contributions by government employees * are work of the U.S. Government and are not subject to copyright * protection in the United States. Portions contributed by government * employees are USGovWork (17USC ยง105). Not subject to copyright. * * Contribution by contractors to the US Veterans Health Administration * during this period are contractually contributed under the * Apache License, Version 2.0. * * See: https://www.usa.gov/government-works * * Contributions prior to 2013: * * Copyright (C) International Health Terminology Standards Development Organisation. * Licensed under the Apache License, Version 2.0. * */ package sh.isaac.provider.workflow.crud; //~--- JDK imports ------------------------------------------------------------ import java.util.Arrays; import java.util.Calendar; import java.util.Date; import java.util.HashSet; import java.util.Set; import java.util.SortedSet; import java.util.UUID; //~--- non-JDK imports -------------------------------------------------------- import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.junit.Assert; import sh.isaac.api.LookupService; import sh.isaac.api.State; import sh.isaac.api.UserRole; import sh.isaac.api.commit.Stamp; import sh.isaac.provider.workflow.WorkflowProvider; import sh.isaac.provider.workflow.model.contents.AvailableAction; import sh.isaac.provider.workflow.model.contents.DefinitionDetail; import sh.isaac.provider.workflow.model.contents.ProcessDetail; import sh.isaac.provider.workflow.model.contents.ProcessDetail.EndWorkflowType; import sh.isaac.provider.workflow.model.contents.ProcessDetail.ProcessStatus; import sh.isaac.provider.workflow.model.contents.ProcessHistory; import sh.isaac.provider.workflow.user.RoleConfigurator; //~--- classes ---------------------------------------------------------------- /** * Test the AbstractWorkflowProviderTestPackage class * * {@link WorkflowProcessInitializerConcluderTest}. * {@link WorkflowAccessorTest}. * {@link WorkflowUpdaterTest}. * * * @author <a href="mailto:jefron@westcoastinformatics.com">Jesse Efron</a> */ public abstract class AbstractWorkflowProviderTestPackage { /** The Constant logger. */ protected static final Logger logger = LogManager.getLogger(); /** The bpmn file path. */ protected static final String BPMN_FILE_PATH = "/sh/isaac/provider/workflow/StaticUnitTestingDefinition.bpmn2"; /** The create role. */ private static UserRole createRole = UserRole.AUTOMATED; /** The Constant TEST_START_TIME. */ /* Constants throughout testclasses to simplify process */ private static final long TEST_START_TIME = new Date().getTime(); /** The Constant conceptsForTesting. */ protected static final Set<Integer> conceptsForTesting = new HashSet<>(Arrays.asList(-55, -56)); /** The Constant LAUNCH_STATE. */ private static final String LAUNCH_STATE = "Ready for Edit"; /** The Constant LAUNCH_ACTION. */ private static final String LAUNCH_ACTION = "Edit"; /** The Constant LAUNCH_OUTCOME. */ private static final String LAUNCH_OUTCOME = "Ready for Review"; /** The Constant LAUNCH_COMMENT. */ private static final String LAUNCH_COMMENT = "Launch Comment"; /** The Constant SEND_TO_APPROVAL_STATE. */ private static final String SEND_TO_APPROVAL_STATE = "Ready for Review"; /** The Constant SEND_TO_APPROVAL_ACTION. */ private static final String SEND_TO_APPROVAL_ACTION = "Review"; /** The Constant SEND_TO_APPROVAL_OUTCOME. */ private static final String SEND_TO_APPROVAL_OUTCOME = "Ready for Approve"; /** The Constant SEND_TO_APPROVAL_COMMENT. */ private static final String SEND_TO_APPROVAL_COMMENT = "Sending for Approval"; /** The Constant REJECT_REVIEW_STATE. */ private static final String REJECT_REVIEW_STATE = "Ready for Review"; /** The Constant REJECT_REVIEW_ACTION. */ private static final String REJECT_REVIEW_ACTION = "Reject QA"; /** The Constant REJECT_REVIEW_OUTCOME. */ private static final String REJECT_REVIEW_OUTCOME = "Ready for Edit"; /** The Constant REJECT_REVIEW_COMMENT. */ private static final String REJECT_REVIEW_COMMENT = "Rejecting QA sending back to Edit"; /** The Constant CONCLUDED_WORKFLOW_COMMENT. */ protected static final String CONCLUDED_WORKFLOW_COMMENT = "Concluded Workflow"; /** The Constant CANCELED_WORKFLOW_COMMENT. */ protected static final String CANCELED_WORKFLOW_COMMENT = "Canceled Workflow"; /** The module seq. */ private static int moduleSeq = 99; /** The path seq. */ private static int pathSeq = 999; /** The conclude action. */ protected static AvailableAction concludeAction; /** The cancel action. */ protected static AvailableAction cancelAction; /** The main definition id. */ /* * Defined by importing definition and static throughout testclasses to * simplify process */ protected static UUID mainDefinitionId; /** The create state. */ private static String createState; /** The create action. */ private static String createAction; /** The create outcome. */ private static String createOutcome; /** The wp. */ protected static WorkflowProvider wp; //~--- methods ------------------------------------------------------------- /** * Adds the components to process. * * @param processId the process id * @param userSeq the user seq * @param state the state */ protected void addComponentsToProcess(UUID processId, int userSeq, State state) { final ProcessDetail entry = wp.getProcessDetailStore() .get(processId); final Stamp s = createStamp(userSeq, state); for (final Integer con: conceptsForTesting) { entry.getComponentToInitialEditMap() .put(con, s); } wp.getProcessDetailStore() .put(processId, entry); } /** * Advance workflow. * * @param processId the process id * @param userId the user id * @param actionRequested the action requested * @param comment the comment * @return true, if successful * @throws Exception the exception */ protected boolean advanceWorkflow(UUID processId, UUID userId, String actionRequested, String comment) throws Exception { return wp.getWorkflowUpdater() .advanceWorkflow(processId, userId, actionRequested, comment, null); } /** * Assert cancel history. * * @param entry the entry * @param processId the process id */ protected void assertCancelHistory(ProcessHistory entry, UUID processId) { Assert.assertEquals(processId, entry.getProcessId()); Assert.assertEquals(RoleConfigurator.getFirstTestUser(), entry.getUserId()); Assert.assertTrue(TEST_START_TIME < entry.getTimeAdvanced()); Assert.assertEquals(cancelAction.getInitialState(), entry.getInitialState()); Assert.assertEquals(cancelAction.getAction(), entry.getAction()); Assert.assertEquals(cancelAction.getOutcomeState(), entry.getOutcomeState()); Assert.assertEquals(CANCELED_WORKFLOW_COMMENT, entry.getComment()); } /** * Assert conclude history. * * @param entry the entry * @param processId the process id */ protected void assertConcludeHistory(ProcessHistory entry, UUID processId) { Assert.assertEquals(processId, entry.getProcessId()); Assert.assertEquals(RoleConfigurator.getFirstTestUser(), entry.getUserId()); Assert.assertTrue(TEST_START_TIME < entry.getTimeAdvanced()); Assert.assertEquals(concludeAction.getInitialState(), entry.getInitialState()); Assert.assertEquals(concludeAction.getAction(), entry.getAction()); Assert.assertEquals(concludeAction.getOutcomeState(), entry.getOutcomeState()); Assert.assertEquals(CONCLUDED_WORKFLOW_COMMENT, entry.getComment()); } /** * Assert history for process. * * @param allProcessHistory the all process history * @param processId the process id */ protected void assertHistoryForProcess(SortedSet<ProcessHistory> allProcessHistory, UUID processId) { int counter = 0; for (final ProcessHistory entry: allProcessHistory) { if (counter == 0) { Assert.assertEquals(processId, entry.getProcessId()); Assert.assertEquals(RoleConfigurator.getFirstTestUser(), entry.getUserId()); Assert.assertTrue(TEST_START_TIME < entry.getTimeAdvanced()); Assert.assertEquals(createState, entry.getInitialState()); Assert.assertEquals(createAction, entry.getAction()); Assert.assertEquals(createOutcome, entry.getOutcomeState()); Assert.assertEquals("", entry.getComment()); } else if (counter == 1) { Assert.assertEquals(processId, entry.getProcessId()); Assert.assertEquals(RoleConfigurator.getFirstTestUser(), entry.getUserId()); Assert.assertTrue(TEST_START_TIME < entry.getTimeAdvanced()); Assert.assertEquals(LAUNCH_STATE, entry.getInitialState()); Assert.assertEquals(LAUNCH_ACTION, entry.getAction()); Assert.assertEquals(LAUNCH_OUTCOME, entry.getOutcomeState()); Assert.assertEquals(LAUNCH_COMMENT, entry.getComment()); } else if (counter == 2) { Assert.assertEquals(processId, entry.getProcessId()); Assert.assertEquals(RoleConfigurator.getFirstTestUser(), entry.getUserId()); Assert.assertTrue(TEST_START_TIME < entry.getTimeAdvanced()); Assert.assertEquals(SEND_TO_APPROVAL_STATE, entry.getInitialState()); Assert.assertEquals(SEND_TO_APPROVAL_ACTION, entry.getAction()); Assert.assertEquals(SEND_TO_APPROVAL_OUTCOME, entry.getOutcomeState()); Assert.assertEquals(SEND_TO_APPROVAL_COMMENT, entry.getComment()); } counter++; } } /** * Cancel workflow. * * @param processId the process id */ protected void cancelWorkflow(UUID processId) { try { Thread.sleep(1); finishWorkflowProcess(processId, cancelAction, RoleConfigurator.getFirstTestUser(), "Canceled Workflow", EndWorkflowType.CANCELED); } catch (final Exception e) { e.printStackTrace(); } } /** * Conclude workflow. * * @param processId the process id */ protected void concludeWorkflow(UUID processId) { try { Thread.sleep(1); finishWorkflowProcess(processId, concludeAction, RoleConfigurator.getFirstTestUser(), "Concluded Workflow", EndWorkflowType.CONCLUDED); } catch (final Exception e) { e.printStackTrace(); } } /** * Creates the first workflow process. * * @param requestedDefinitionId the requested definition id * @return the uuid */ protected UUID createFirstWorkflowProcess(UUID requestedDefinitionId) { return createWorkflowProcess(requestedDefinitionId, "Main Process Name", "Main Process Description"); } /** * Creates the second workflow process. * * @param requestedDefinitionId the requested definition id * @return the uuid */ protected UUID createSecondWorkflowProcess(UUID requestedDefinitionId) { return createWorkflowProcess(requestedDefinitionId, "Secondary Process Name", "Secondary Process Description"); } /** * Creates the secondary definition. * * @return the uuid */ protected UUID createSecondaryDefinition() { final Set<UserRole> roles = new HashSet<>(); roles.add(UserRole.EDITOR); roles.add(UserRole.REVIEWER); roles.add(UserRole.APPROVER); final DefinitionDetail createdEntry = new DefinitionDetail("BPMN2 ID-X", "JUnit BPMN2", "Testing", "1.0", roles, "Description of BPMN2 ID-X"); final UUID defId = wp.getDefinitionDetailStore() .add(createdEntry); // Duplicate AvailableActions final Set<AvailableAction> actionsToAdd = new HashSet<>(); for (final AvailableAction action: wp.getAvailableActionStore() .values()) { actionsToAdd.add(new AvailableAction(defId, action.getInitialState(), action.getAction(), action.getOutcomeState(), action.getRole())); } for (final AvailableAction action: actionsToAdd) { wp.getAvailableActionStore() .add(action); } return defId; } /** * Creates the stamp. * * @param userSeq the user seq * @param state the state * @return the stamp */ protected Stamp createStamp(int userSeq, State state) { return new Stamp(state, new Date().getTime(), userSeq, moduleSeq, pathSeq); } /** * End workflow process. * * @param processId the process id * @param actionToProcess the action to process * @param userId the user id * @param comment the comment * @param endType the end type * @throws Exception the exception */ protected void endWorkflowProcess(UUID processId, AvailableAction actionToProcess, UUID userId, String comment, EndWorkflowType endType) throws Exception { wp.getWorkflowProcessInitializerConcluder() .endWorkflowProcess(processId, actionToProcess, userId, comment, endType, null); } /** * Execute launch workflow. * * @param processId the process id */ protected void executeLaunchWorkflow(UUID processId) { try { Thread.sleep(1); final ProcessDetail entry = wp.getProcessDetailStore() .get(processId); entry.setStatus(ProcessStatus.LAUNCHED); entry.setTimeLaunched(new Date().getTime()); wp.getProcessDetailStore() .put(processId, entry); } catch (final InterruptedException e) { throw new RuntimeException(e); } } /** * Execute reject review advancement. * * @param requestedProcessId the requested process id */ protected void executeRejectReviewAdvancement(UUID requestedProcessId) { try { Thread.sleep(1); int historySequence = 1; if (wp.getWorkflowAccessor() .getProcessHistory(requestedProcessId) != null) { historySequence = wp.getWorkflowAccessor() .getProcessHistory(requestedProcessId) .last() .getHistorySequence(); } final ProcessHistory entry = new ProcessHistory(requestedProcessId, RoleConfigurator.getFirstTestUser(), new Date().getTime(), REJECT_REVIEW_STATE, REJECT_REVIEW_ACTION, REJECT_REVIEW_OUTCOME, REJECT_REVIEW_COMMENT, historySequence + 1); wp.getProcessHistoryStore() .add(entry); } catch (final InterruptedException e) { throw new RuntimeException(e); } } /** * Execute send for approval advancement. * * @param requestedProcessId the requested process id */ protected void executeSendForApprovalAdvancement(UUID requestedProcessId) { try { Thread.sleep(1); int historySequence = 1; if (wp.getWorkflowAccessor() .getProcessHistory(requestedProcessId) != null) { historySequence = wp.getWorkflowAccessor() .getProcessHistory(requestedProcessId) .last() .getHistorySequence(); } final ProcessHistory entry = new ProcessHistory(requestedProcessId, RoleConfigurator.getFirstTestUser(), new Date().getTime(), SEND_TO_APPROVAL_STATE, SEND_TO_APPROVAL_ACTION, SEND_TO_APPROVAL_OUTCOME, SEND_TO_APPROVAL_COMMENT, historySequence + 1); wp.getProcessHistoryStore() .add(entry); } catch (final InterruptedException e) { throw new RuntimeException(e); } } /** * Execute send for review advancement. * * @param processId the process id */ protected void executeSendForReviewAdvancement(UUID processId) { final ProcessDetail entry = wp.getProcessDetailStore() .get(processId); try { Thread.sleep(1); int historySequence = 1; if (wp.getWorkflowAccessor() .getProcessHistory(processId) != null) { historySequence = wp.getWorkflowAccessor() .getProcessHistory(processId) .last() .getHistorySequence(); } final ProcessHistory advanceEntry = new ProcessHistory(processId, entry.getCreatorId(), new Date().getTime(), LAUNCH_STATE, LAUNCH_ACTION, LAUNCH_OUTCOME, LAUNCH_COMMENT, historySequence + 1); wp.getProcessHistoryStore() .add(advanceEntry); } catch (final InterruptedException e) { throw new RuntimeException(e); } } /** * Global setup. */ protected static void globalSetup() { RoleConfigurator.configureForTest(); wp = LookupService.get() .getService(WorkflowProvider.class); mainDefinitionId = wp.getBPMNInfo() .getDefinitionId(); final AvailableAction startNodeAction = wp.getBPMNInfo() .getDefinitionStartActionMap() .get(mainDefinitionId) .iterator() .next(); createState = startNodeAction.getInitialState(); createAction = startNodeAction.getAction(); createOutcome = startNodeAction.getOutcomeState(); cancelAction = wp.getBPMNInfo() .getEndWorkflowTypeMap() .get(EndWorkflowType.CONCLUDED) .iterator() .next(); concludeAction = wp.getBPMNInfo() .getEndWorkflowTypeMap() .get(EndWorkflowType.CONCLUDED) .iterator() .next(); } /** * Time since yesterday before tomorrow. * * @param time the time * @return true, if successful */ protected boolean timeSinceYesterdayBeforeTomorrow(long time) { Calendar cal = Calendar.getInstance(); cal.add(Calendar.DATE, -1); final long yesterdayTimestamp = cal.getTimeInMillis(); cal = Calendar.getInstance(); cal.add(Calendar.DATE, 1); final long tomorrowTimestamp = cal.getTimeInMillis(); return (time >= yesterdayTimestamp) && (time <= tomorrowTimestamp); } /** * Creates the workflow process. * * @param requestedDefinitionId the requested definition id * @param name the name * @param description the description * @return the uuid */ private UUID createWorkflowProcess(UUID requestedDefinitionId, String name, String description) { // Mimick the initConcluder's create new process final ProcessDetail details = new ProcessDetail(requestedDefinitionId, RoleConfigurator.getFirstTestUser(), new Date().getTime(), ProcessStatus.DEFINED, name, description); final UUID processId = wp.getProcessDetailStore() .add(details); // Add Process History with START_STATE-AUTOMATED-EDIT_STATE final AvailableAction startAdvancement = new AvailableAction(requestedDefinitionId, createState, createAction, createOutcome, createRole); final ProcessHistory advanceEntry = new ProcessHistory(processId, RoleConfigurator.getFirstTestUser(), new Date().getTime(), startAdvancement.getInitialState(), startAdvancement.getAction(), startAdvancement.getOutcomeState(), "", 0); wp.getProcessHistoryStore() .add(advanceEntry); return processId; } /** * Finish workflow process. * * @param processId the process id * @param actionToProcess the action to process * @param userId the user id * @param comment the comment * @param endType the end type * @throws Exception the exception */ private void finishWorkflowProcess(UUID processId, AvailableAction actionToProcess, UUID userId, String comment, EndWorkflowType endType) throws Exception { // Mimick the initConcluder's finish workflow process final ProcessDetail entry = wp.getProcessDetailStore() .get(processId); if (endType.equals(EndWorkflowType.CANCELED)) { entry.setStatus(ProcessStatus.CANCELED); } else if (endType.equals(EndWorkflowType.CONCLUDED)) { entry.setStatus(ProcessStatus.CONCLUDED); } entry.setTimeCanceledOrConcluded(new Date().getTime()); wp.getProcessDetailStore() .put(processId, entry); // Only add Cancel state in Workflow if process has already been // launched int historySequence = 1; if (wp.getWorkflowAccessor() .getProcessHistory(processId) != null) { historySequence = wp.getWorkflowAccessor() .getProcessHistory(processId) .last() .getHistorySequence(); } final ProcessHistory advanceEntry = new ProcessHistory(processId, userId, new Date().getTime(), actionToProcess.getInitialState(), actionToProcess.getAction(), actionToProcess.getOutcomeState(), comment, historySequence + 1); wp.getProcessHistoryStore() .add(advanceEntry); if (endType.equals(EndWorkflowType.CANCELED)) { // TODO: Handle cancelation store and handle reverting automatically } } }