/* * Copyright (c) 2010-2017 Evolveum * * 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. */ package com.evolveum.midpoint.testing.story; import com.evolveum.midpoint.audit.api.AuditEventRecord; import com.evolveum.midpoint.audit.api.AuditEventStage; import com.evolveum.midpoint.audit.api.AuditEventType; import com.evolveum.midpoint.model.api.WorkflowService; import com.evolveum.midpoint.model.test.DummyTransport; import com.evolveum.midpoint.notifications.api.transports.Message; import com.evolveum.midpoint.prism.PrismContext; import com.evolveum.midpoint.prism.PrismObject; import com.evolveum.midpoint.prism.PrismReference; import com.evolveum.midpoint.prism.PrismReferenceValue; import com.evolveum.midpoint.prism.delta.ItemDelta; import com.evolveum.midpoint.prism.delta.ObjectDelta; import com.evolveum.midpoint.prism.delta.builder.DeltaBuilder; import com.evolveum.midpoint.prism.path.ItemPath; import com.evolveum.midpoint.prism.util.PrismAsserts; import com.evolveum.midpoint.prism.util.PrismUtil; import com.evolveum.midpoint.prism.xml.XmlTypeConverter; import com.evolveum.midpoint.schema.ObjectDeltaOperation; import com.evolveum.midpoint.schema.SearchResultList; import com.evolveum.midpoint.schema.constants.ObjectTypes; import com.evolveum.midpoint.schema.constants.SchemaConstants; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.schema.util.ObjectTypeUtil; import com.evolveum.midpoint.schema.util.WfContextUtil; import com.evolveum.midpoint.task.api.Task; import com.evolveum.midpoint.test.util.TestUtil; import com.evolveum.midpoint.util.DebugUtil; import com.evolveum.midpoint.util.exception.ObjectNotFoundException; import com.evolveum.midpoint.util.exception.SchemaException; import com.evolveum.midpoint.wf.api.WorkflowConstants; import com.evolveum.midpoint.wf.util.ApprovalUtils; import com.evolveum.midpoint.xml.ns._public.common.common_3.*; import org.apache.commons.collections4.multimap.ArrayListValuedHashMap; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.annotation.DirtiesContext.ClassMode; import org.springframework.test.context.ContextConfiguration; import org.testng.annotations.Test; import javax.xml.namespace.QName; import java.io.File; import java.util.*; import java.util.stream.Collectors; import static com.evolveum.midpoint.prism.util.PrismAsserts.assertReferenceValues; import static com.evolveum.midpoint.test.IntegrationTestTools.display; import static org.testng.AssertJUnit.*; /** * * @author mederly * */ @SuppressWarnings("FieldCanBeLocal") @ContextConfiguration(locations = {"classpath:ctx-story-test-main.xml"}) @DirtiesContext(classMode = ClassMode.AFTER_CLASS) public class TestStrings extends AbstractStoryTest { @Autowired private WorkflowService workflowService; @Autowired private DummyTransport dummyTransport; private static final String TEST_DIR = "src/test/resources/strings"; private static final String ORG_DIR = TEST_DIR + "/orgs"; private static final String ROLES_DIR = TEST_DIR + "/roles"; private static final String ROLES_SPECIFIC_DIR = TEST_DIR + "/roles-specific"; private static final String USERS_DIR = TEST_DIR + "/users"; private static final File ORG_MONKEY_ISLAND_FILE = new File(ORG_DIR, "0-org-monkey-island-modified.xml"); private static final File ORG_TEAMS_FILE = new File(ORG_DIR, "1-teams.xml"); private static String orgTeamsOid; private static final File ORG_ROLE_CATALOG_FILE = new File(ORG_DIR, "2-role-catalog.xml"); private static String orgRoleCatalogOid; private static final File ORG_SECURITY_APPROVERS_FILE = new File(ORG_DIR, "security-approvers.xml"); private static String orgSecurityApproversOid; private static final File ORG_SOD_APPROVERS_FILE = new File(ORG_DIR, "sod-approvers.xml"); private static String orgSodApproversOid; private static final File FORM_USER_DETAILS_FILE = new File(ROLES_DIR, "form-user-details.xml"); private static String formUserDetailsOid; private static final File METAROLE_APPROVAL_ROLE_APPROVERS_FIRST_FILE = new File(ROLES_DIR, "metarole-approval-role-approvers-first.xml"); private static String metaroleApprovalRoleApproversFirstOid; private static final File METAROLE_APPROVAL_ROLE_APPROVERS_FORM_FILE = new File(ROLES_DIR, "metarole-approval-role-approvers-form.xml"); private static String metaroleApprovalRoleApproversFormOid; private static final File METAROLE_APPROVAL_SECURITY_FILE = new File(ROLES_DIR, "metarole-approval-security.xml"); private static String metaroleApprovalSecurityOid; private static final File ROLE_A_TEST_1 = new File(ROLES_SPECIFIC_DIR, "a-test-1.xml"); private static String roleATest1Oid; private static final File ROLE_A_TEST_2A = new File(ROLES_SPECIFIC_DIR, "a-test-2a.xml"); private static String roleATest2aOid; private static final File ROLE_A_TEST_2B = new File(ROLES_SPECIFIC_DIR, "a-test-2b.xml"); private static String roleATest2bOid; private static final File ROLE_A_TEST_3A = new File(ROLES_SPECIFIC_DIR, "a-test-3a.xml"); private static String roleATest3aOid; private static final File ROLE_A_TEST_3B = new File(ROLES_SPECIFIC_DIR, "a-test-3b.xml"); private static String roleATest3bOid; private static final File ROLE_A_TEST_3X = new File(ROLES_SPECIFIC_DIR, "a-test-3x.xml"); private static String roleATest3xOid; private static final File ROLE_A_TEST_3Y = new File(ROLES_SPECIFIC_DIR, "a-test-3y.xml"); private static String roleATest3yOid; private static final File ROLE_A_TEST_4 = new File(ROLES_SPECIFIC_DIR, "a-test-4.xml"); private static String roleATest4Oid; private static final File USER_BARKEEPER_FILE = new File(USERS_DIR, "barkeeper.xml"); private static String userBarkeeperOid; private static final File USER_BOB_FILE = new File(USERS_DIR, "bob.xml"); private static String userBobOid; private static final File USER_CARLA_FILE = new File(USERS_DIR, "carla.xml"); private static String userCarlaOid; private static final File USER_CHEESE_FILE = new File(USERS_DIR, "cheese.xml"); private static String userCheeseOid; private static final File USER_CHEF_FILE = new File(USERS_DIR, "chef.xml"); private static String userChefOid; private static final File USER_ELAINE_FILE = new File(USERS_DIR, "elaine.xml"); private static String userElaineOid; private static final File USER_GUYBRUSH_FILE = new File(USERS_DIR, "guybrush.xml"); private static String userGuybrushOid; private static final File USER_LECHUCK_FILE = new File(USERS_DIR, "lechuck.xml"); private static String userLechuckOid; private static final File CONFIG_WITH_GLOBAL_RULES_FILE = new File(ROLES_DIR, "global-policy-rules.xml"); public static final String NS_STRINGS_EXT = "http://midpoint.evolveum.com/xml/ns/strings"; private static final String DUMMY_WORK_ITEM_LIFECYCLE = "dummy:workItemLifecycle"; private static final String DUMMY_WORK_ITEM_ALLOCATION = "dummy:workItemAllocation"; private static final String DUMMY_WORK_ITEM_CUSTOM = "dummy:workItemCustom"; private static final String DUMMY_PROCESS = "dummy:process"; @Override public void initSystem(Task initTask, OperationResult initResult) throws Exception { super.initSystem(initTask, initResult); // copy rules from the file into live system config object PrismObject<SystemConfigurationType> rules = prismContext.parserFor(CONFIG_WITH_GLOBAL_RULES_FILE).parse(); repositoryService.modifyObject(SystemConfigurationType.class, SystemObjectsType.SYSTEM_CONFIGURATION.value(), DeltaBuilder.deltaFor(SystemConfigurationType.class, prismContext) .item(SystemConfigurationType.F_GLOBAL_POLICY_RULE).add( rules.asObjectable().getGlobalPolicyRule().stream() .map(r -> r.clone().asPrismContainerValue()) .collect(Collectors.toList())) .asItemDeltas(), initResult); // we prefer running trigger scanner by hand resetTriggerTask(TASK_TRIGGER_SCANNER_OID, TASK_TRIGGER_SCANNER_FILE, initResult); // and we don't need validity scanner taskManager.suspendAndDeleteTasks(Collections.singletonList(TASK_VALIDITY_SCANNER_OID), 60000L, true, initResult); Task triggerScanner = taskManager.getTask(TASK_TRIGGER_SCANNER_OID, initResult); display("triggerScanner", triggerScanner); // import of story objects repoAddObjectsFromFile(ORG_MONKEY_ISLAND_FILE, OrgType.class, initResult); orgTeamsOid = repoAddObjectFromFile(ORG_TEAMS_FILE, initResult).getOid(); orgRoleCatalogOid = repoAddObjectFromFile(ORG_ROLE_CATALOG_FILE, initResult).getOid(); orgSecurityApproversOid = repoAddObjectFromFile(ORG_SECURITY_APPROVERS_FILE, initResult).getOid(); orgSodApproversOid = repoAddObjectFromFile(ORG_SOD_APPROVERS_FILE, initResult).getOid(); formUserDetailsOid = repoAddObjectFromFile(FORM_USER_DETAILS_FILE, initResult).getOid(); metaroleApprovalRoleApproversFirstOid = repoAddObjectFromFile(METAROLE_APPROVAL_ROLE_APPROVERS_FIRST_FILE, initResult).getOid(); metaroleApprovalRoleApproversFormOid = repoAddObjectFromFile(METAROLE_APPROVAL_ROLE_APPROVERS_FORM_FILE, initResult).getOid(); metaroleApprovalSecurityOid = repoAddObjectFromFile(METAROLE_APPROVAL_SECURITY_FILE, initResult).getOid(); roleATest1Oid = addAndRecompute(ROLE_A_TEST_1, initTask, initResult); roleATest2aOid = addAndRecompute(ROLE_A_TEST_2A, initTask, initResult); roleATest2bOid = addAndRecompute(ROLE_A_TEST_2B, initTask, initResult); roleATest3aOid = addAndRecompute(ROLE_A_TEST_3A, initTask, initResult); roleATest3bOid = addAndRecompute(ROLE_A_TEST_3B, initTask, initResult); roleATest3xOid = addAndRecompute(ROLE_A_TEST_3X, initTask, initResult); roleATest3yOid = addAndRecompute(ROLE_A_TEST_3Y, initTask, initResult); roleATest4Oid = addAndRecompute(ROLE_A_TEST_4, initTask, initResult); userBarkeeperOid = addAndRecomputeUser(USER_BARKEEPER_FILE, initTask, initResult); userBobOid = addAndRecomputeUser(USER_BOB_FILE, initTask, initResult); userCarlaOid = addAndRecomputeUser(USER_CARLA_FILE, initTask, initResult); userCheeseOid = addAndRecomputeUser(USER_CHEESE_FILE, initTask, initResult); userChefOid = addAndRecomputeUser(USER_CHEF_FILE, initTask, initResult); userElaineOid = addAndRecomputeUser(USER_ELAINE_FILE, initTask, initResult); userGuybrushOid = addAndRecomputeUser(USER_GUYBRUSH_FILE, initTask, initResult); userLechuckOid = addAndRecomputeUser(USER_LECHUCK_FILE, initTask, initResult); DebugUtil.setPrettyPrintBeansAs(PrismContext.LANG_YAML); } @Override protected PrismObject<UserType> getDefaultActor() { return userAdministrator; } @Test public void test000Sanity() throws Exception { final String TEST_NAME = "test000Sanity"; TestUtil.displayTestTile(this, TEST_NAME); Task task = createTask(TestStrings.class.getName() + "." + TEST_NAME); // TODO } //region Basic approval @Test public void test100SimpleAssignmentStart() throws Exception { final String TEST_NAME = "test100SimpleAssignmentStart"; TestUtil.displayTestTile(this, TEST_NAME); Task task = createTask(TestStrings.class.getName() + "." + TEST_NAME); OperationResult result = task.getResult(); dummyAuditService.clear(); dummyTransport.clearMessages(); // WHEN assignRole(userBobOid, roleATest1Oid, task, task.getResult()); // THEN assertNotAssignedRole(getUser(userBobOid), roleATest1Oid); WorkItemType workItem = getWorkItem(task, result); display("Work item", workItem); PrismObject<TaskType> wfTask = getTask(WfContextUtil.getTask(workItem).getOid()); display("wfTask", wfTask); assertTriggers(wfTask, 2); ItemApprovalProcessStateType info = WfContextUtil.getItemApprovalProcessInfo(wfTask.asObjectable().getWorkflowContext()); ApprovalSchemaType schema = info.getApprovalSchema(); assertEquals("Wrong # of approval levels", 3, schema.getStage().size()); assertApprovalLevel(schema, 1, "Line managers", "P5D", 2); assertApprovalLevel(schema, 2, "Security", "P7D", 1); assertApprovalLevel(schema, 3, "Role approvers (all)", "P5D", 2); assertStage(wfTask, 1, 3, "Line managers", null); assertAssignee(workItem, userLechuckOid, userLechuckOid); List<Message> lifecycleMessages = dummyTransport.getMessages(DUMMY_WORK_ITEM_LIFECYCLE); List<Message> allocationMessages = dummyTransport.getMessages(DUMMY_WORK_ITEM_ALLOCATION); List<Message> processMessages = dummyTransport.getMessages(DUMMY_PROCESS); display("work items lifecycle notifications", lifecycleMessages); display("work items allocation notifications", allocationMessages); display("processes notifications", processMessages); assertEquals("Wrong # of work items lifecycle messages", 1, lifecycleMessages.size()); assertMessage(lifecycleMessages.get(0), "lechuck@evolveum.com", "A new work item has been created", "Stage: Line managers (1/3)", "Allocated to: Captain LeChuck (lechuck)", "(in 5 days)"); assertEquals("Wrong # of work items allocation messages", 1, allocationMessages.size()); assertMessage(allocationMessages.get(0), "lechuck@evolveum.com", "Work item has been allocated to you", "Stage: Line managers (1/3)", "Allocated to: Captain LeChuck (lechuck)", "(in 5 days)"); assertEquals("Wrong # of process messages", 1, processMessages.size()); assertMessage(processMessages.get(0), "administrator@evolveum.com", "Workflow process instance has been started", "Process instance name: Assigning a-test-1 to bob", "Stage: Line managers (1/3)"); display("audit", dummyAuditService); } @Test public void test102SimpleAssignmentApproveByLechuck() throws Exception { final String TEST_NAME = "test102SimpleAssignmentApproveByLechuck"; TestUtil.displayTestTile(this, TEST_NAME); Task task = createTask(TestStrings.class.getName() + "." + TEST_NAME); OperationResult result = task.getResult(); dummyAuditService.clear(); dummyTransport.clearMessages(); // GIVEN login(userAdministrator); WorkItemType workItem = getWorkItem(task, result); // WHEN PrismObject<UserType> lechuck = getUserFromRepo(userLechuckOid); login(lechuck); workflowService.completeWorkItem(workItem.getExternalId(), true, "OK. LeChuck", null, result); // THEN login(userAdministrator); List<WorkItemType> workItems = getWorkItems(task, result); assertEquals("Wrong # of work items on level 2", 2, workItems.size()); displayWorkItems("Work item after 1st approval", workItems); PrismObject<TaskType> wfTask = getTask(WfContextUtil.getTask(workItem).getOid()); display("wfTask after 1st approval", wfTask); assertStage(wfTask, 2, 3, "Security", null); assertTriggers(wfTask, 4); // notifications List<Message> lifecycleMessages = dummyTransport.getMessages(DUMMY_WORK_ITEM_LIFECYCLE); List<Message> allocationMessages = dummyTransport.getMessages(DUMMY_WORK_ITEM_ALLOCATION); List<Message> processMessages = dummyTransport.getMessages(DUMMY_PROCESS); display("work items lifecycle notifications", lifecycleMessages); display("work items allocation notifications", allocationMessages); display("processes notifications", processMessages); dummyTransport.clearMessages(); assertEquals("Wrong # of work items lifecycle messages", 3, lifecycleMessages.size()); assertEquals("Wrong # of work items allocation messages", 3, allocationMessages.size()); assertNull("process messages", processMessages); Map<String,Message> sorted = sortByRecipientsSingle(lifecycleMessages); assertMessage(sorted.get("lechuck@evolveum.com"), "lechuck@evolveum.com", "Work item has been completed", "Work item: Approve assigning a-test-1 to bob", "Stage: Line managers (1/3)", "Allocated to: Captain LeChuck (lechuck)", "Result: APPROVED", "^Deadline:"); assertMessage(sorted.get("elaine@evolveum.com"), "elaine@evolveum.com", "A new work item has been created", "Work item: Approve assigning a-test-1 to bob", "Stage: Security (2/3)", "Allocated to: Elaine Marley (elaine)", "(in 7 days)", "^Result:"); assertMessage(sorted.get("barkeeper@evolveum.com"), "barkeeper@evolveum.com", "A new work item has been created", "Work item: Approve assigning a-test-1 to bob", "Stage: Security (2/3)", "Allocated to: Horridly Scarred Barkeep (barkeeper)", "(in 7 days)", "^Result:"); Map<String,Message> sorted2 = sortByRecipientsSingle(allocationMessages); assertMessage(sorted2.get("lechuck@evolveum.com"), "lechuck@evolveum.com", "Work item has been completed", "Work item: Approve assigning a-test-1 to bob", "Stage: Line managers (1/3)", "Allocated to: Captain LeChuck (lechuck)", "Result: APPROVED", "^Deadline:"); assertMessage(sorted2.get("elaine@evolveum.com"), "elaine@evolveum.com", "Work item has been allocated to you", "Work item: Approve assigning a-test-1 to bob", "Stage: Security (2/3)", "Allocated to: Elaine Marley (elaine)", "(in 7 days)", "^Result:"); assertMessage(sorted2.get("barkeeper@evolveum.com"), "barkeeper@evolveum.com", "Work item has been allocated to you", "Work item: Approve assigning a-test-1 to bob", "Stage: Security (2/3)", "Allocated to: Horridly Scarred Barkeep (barkeeper)", "(in 7 days)", "^Result:"); // events List<CaseEventType> events = assertEvents(wfTask, 2); assertCompletionEvent(events.get(1), userLechuckOid, userLechuckOid, 1, "Line managers", WorkItemOutcomeType.APPROVE, "OK. LeChuck"); display("audit", dummyAuditService); } @Test public void test104SimpleAssignmentApproveByAdministrator() throws Exception { final String TEST_NAME = "test104SimpleAssignmentApproveByAdministrator"; TestUtil.displayTestTile(this, TEST_NAME); Task task = createTask(TestStrings.class.getName() + "." + TEST_NAME); OperationResult result = task.getResult(); dummyAuditService.clear(); dummyTransport.clearMessages(); // GIVEN login(userAdministrator); List<WorkItemType> workItems = getWorkItems(task, result); WorkItemType firstWorkItem = workItems.get(0); // WHEN // Second approval workflowService.completeWorkItem(firstWorkItem.getExternalId(), true, "OK. Security.", null, result); // THEN workItems = getWorkItems(task, result); displayWorkItems("Work item after 2nd approval", workItems); assertEquals("Wrong # of work items on level 3", 2, workItems.size()); PrismObject<TaskType> wfTask = getTask(WfContextUtil.getTask(workItems.get(0)).getOid()); display("wfTask after 2nd approval", wfTask); assertStage(wfTask, 3, 3, "Role approvers (all)", null); assertTriggers(wfTask, 4); Map<String, WorkItemType> workItemsMap = sortByOriginalAssignee(workItems); assertNotNull("chef is not an approver", workItemsMap.get(userChefOid)); assertNotNull("cheese is not an approver", workItemsMap.get(userCheeseOid)); List<Message> lifecycleMessages = dummyTransport.getMessages(DUMMY_WORK_ITEM_LIFECYCLE); List<Message> allocationMessages = dummyTransport.getMessages(DUMMY_WORK_ITEM_ALLOCATION); List<Message> processMessages = dummyTransport.getMessages(DUMMY_PROCESS); display("work items lifecycle notifications", lifecycleMessages); display("work items allocation notifications", allocationMessages); display("processes notifications", processMessages); dummyTransport.clearMessages(); assertEquals("Wrong # of work items lifecycle messages", 4, lifecycleMessages.size()); assertEquals("Wrong # of work items allocation messages", 4, allocationMessages.size()); assertNull("process messages", processMessages); Map<String,Message> sorted = sortByRecipientsSingle(lifecycleMessages); assertMessage(sorted.get("elaine@evolveum.com"), "elaine@evolveum.com", "Work item has been completed", "Work item: Approve assigning a-test-1 to bob", "Stage: Security (2/3)", "Allocated to: Elaine Marley (elaine)", "Carried out by: midPoint Administrator (administrator)", "Result: APPROVED", "^Deadline:"); assertMessage(sorted.get("barkeeper@evolveum.com"), "barkeeper@evolveum.com", "Work item has been cancelled", "Work item: Approve assigning a-test-1 to bob", "Stage: Security (2/3)", "Allocated to: Horridly Scarred Barkeep (barkeeper)", "^Result:", "^Deadline:", "^Carried out by:"); assertMessage(sorted.get("cheese@evolveum.com"), "cheese@evolveum.com", "A new work item has been created", "Work item: Approve assigning a-test-1 to bob", "Role approvers (all) (3/3)", "Allocated to: Ignatius Cheese (cheese)", "^Result:", "(in 5 days)"); assertMessage(sorted.get("chef@evolveum.com"), "chef@evolveum.com", "A new work item has been created", "Work item: Approve assigning a-test-1 to bob", "Role approvers (all) (3/3)", "Allocated to: Scumm Bar Chef (chef)", "^Result:", "(in 5 days)"); Map<String,Message> sorted2 = sortByRecipientsSingle(allocationMessages); assertMessage(sorted2.get("elaine@evolveum.com"), "elaine@evolveum.com", "Work item has been completed", "Work item: Approve assigning a-test-1 to bob", "Stage: Security (2/3)", "Allocated to: Elaine Marley (elaine)", "Carried out by: midPoint Administrator (administrator)", "Result: APPROVED", "^Deadline:"); assertMessage(sorted2.get("barkeeper@evolveum.com"), "barkeeper@evolveum.com", "Work item has been cancelled", "Work item: Approve assigning a-test-1 to bob", "Stage: Security (2/3)", "Allocated to: Horridly Scarred Barkeep (barkeeper)", "^Result:", "^Deadline:", "^Carried out by:"); assertMessage(sorted2.get("cheese@evolveum.com"), "cheese@evolveum.com", "Work item has been allocated to you", "Work item: Approve assigning a-test-1 to bob", "Role approvers (all) (3/3)", "Allocated to: Ignatius Cheese (cheese)", "^Result:", "(in 5 days)"); assertMessage(sorted2.get("chef@evolveum.com"), "chef@evolveum.com", "Work item has been allocated to you", "Work item: Approve assigning a-test-1 to bob", "Role approvers (all) (3/3)", "Allocated to: Scumm Bar Chef (chef)", "^Result:", "(in 5 days)"); display("audit", dummyAuditService); } @Test public void test106SimpleAssignmentApproveByCheese() throws Exception { final String TEST_NAME = "test106SimpleAssignmentApproveByCheese"; TestUtil.displayTestTile(this, TEST_NAME); Task task = createTask(TestStrings.class.getName() + "." + TEST_NAME); OperationResult result = task.getResult(); dummyAuditService.clear(); dummyTransport.clearMessages(); // GIVEN login(userAdministrator); List<WorkItemType> workItems = getWorkItems(task, result); Map<String, WorkItemType> workItemsMap = sortByOriginalAssignee(workItems); // WHEN login(getUser(userCheeseOid)); workflowService.completeWorkItem(workItemsMap.get(userCheeseOid).getExternalId(), true, "OK. Cheese.", null, result); // THEN login(userAdministrator); workItems = getWorkItems(task, result); displayWorkItems("Work item after 3rd approval", workItems); assertEquals("Wrong # of work items on level 3", 1, workItems.size()); workItemsMap = sortByOriginalAssignee(workItems); PrismObject<TaskType> wfTask = getTask(WfContextUtil.getTask(workItems.get(0)).getOid()); display("wfTask after 3rd approval", wfTask); assertStage(wfTask, 3, 3, "Role approvers (all)", null); assertTriggers(wfTask, 2); assertNotNull("chef is not an approver", workItemsMap.get(userChefOid)); List<Message> lifecycleMessages = dummyTransport.getMessages(DUMMY_WORK_ITEM_LIFECYCLE); List<Message> allocationMessages = dummyTransport.getMessages(DUMMY_WORK_ITEM_ALLOCATION); List<Message> processMessages = dummyTransport.getMessages(DUMMY_PROCESS); display("work items lifecycle notifications", lifecycleMessages); display("work items allocation notifications", allocationMessages); display("processes notifications", processMessages); dummyTransport.clearMessages(); assertEquals("Wrong # of work items lifecycle messages", 1, lifecycleMessages.size()); assertEquals("Wrong # of work items allocation messages", 1, allocationMessages.size()); assertNull("process messages", processMessages); assertMessage(lifecycleMessages.get(0), "cheese@evolveum.com", "Work item has been completed", "Work item: Approve assigning a-test-1 to bob", "Role approvers (all) (3/3)", "Allocated to: Ignatius Cheese (cheese)", "Carried out by: Ignatius Cheese (cheese)", "Result: APPROVED", "^Deadline:"); assertMessage(allocationMessages.get(0), "cheese@evolveum.com", "Work item has been completed", "Work item: Approve assigning a-test-1 to bob", "Role approvers (all) (3/3)", "Allocated to: Ignatius Cheese (cheese)", "Carried out by: Ignatius Cheese (cheese)", "Result: APPROVED", "^Deadline:"); display("audit", dummyAuditService); } @Test public void test108SimpleAssignmentApproveByChef() throws Exception { final String TEST_NAME = "test108SimpleAssignmentApproveByChef"; TestUtil.displayTestTile(this, TEST_NAME); Task task = createTask(TestStrings.class.getName() + "." + TEST_NAME); OperationResult result = task.getResult(); // GIVEN login(userAdministrator); List<WorkItemType> workItems = getWorkItems(task, result); String taskOid = WfContextUtil.getTask(workItems.get(0)).getOid(); Map<String, WorkItemType> workItemsMap = sortByOriginalAssignee(workItems); // WHEN login(getUser(userChefOid)); String workItemId = workItemsMap.get(userChefOid).getExternalId(); workflowService.completeWorkItem(workItemId, true, "OK. Chef.", null, result); // THEN login(userAdministrator); workItems = getWorkItems(task, result); displayWorkItems("Work item after 4th approval", workItems); assertEquals("Wrong # of work items on level 3", 0, workItems.size()); PrismObject<TaskType> wfTask = getTask(taskOid); display("wfTask after 4th approval", wfTask); Task parent = getParentTask(wfTask, result); waitForTaskFinish(parent, true, 60000); assertAssignedRole(getUser(userBobOid), roleATest1Oid); assertTriggers(wfTask, 0); List<Message> lifecycleMessages = dummyTransport.getMessages(DUMMY_WORK_ITEM_LIFECYCLE); List<Message> allocationMessages = dummyTransport.getMessages(DUMMY_WORK_ITEM_ALLOCATION); List<Message> processMessages = dummyTransport.getMessages(DUMMY_PROCESS); display("work items lifecycle notifications", lifecycleMessages); display("work items allocation notifications", allocationMessages); display("processes notifications", processMessages); dummyTransport.clearMessages(); assertEquals("Wrong # of work items lifecycle messages", 1, lifecycleMessages.size()); assertEquals("Wrong # of work items allocation messages", 1, allocationMessages.size()); assertEquals("Wrong # of process messages", 1, processMessages.size()); assertMessage(lifecycleMessages.get(0), "chef@evolveum.com", "Work item has been completed", "Work item: Approve assigning a-test-1 to bob", "Role approvers (all) (3/3)", "Allocated to: Scumm Bar Chef (chef)", "Carried out by: Scumm Bar Chef (chef)", "Result: APPROVED", "^Deadline:"); assertMessage(allocationMessages.get(0), "chef@evolveum.com", "Work item has been completed", "Work item: Approve assigning a-test-1 to bob", "Role approvers (all) (3/3)", "Allocated to: Scumm Bar Chef (chef)", "Carried out by: Scumm Bar Chef (chef)", "Result: APPROVED", "^Deadline:"); assertMessage(processMessages.get(0), "administrator@evolveum.com", "Workflow process instance has finished", "Process instance name: Assigning a-test-1 to bob", "Result: APPROVED"); display("audit", dummyAuditService); List<AuditEventRecord> workItemEvents = filter(getParamAuditRecords( WorkflowConstants.AUDIT_WORK_ITEM_ID, workItemId, result), AuditEventStage.EXECUTION); assertAuditReferenceValue(workItemEvents, WorkflowConstants.AUDIT_OBJECT, userBobOid, UserType.COMPLEX_TYPE, "bob"); assertAuditTarget(workItemEvents.get(0), userBobOid, UserType.COMPLEX_TYPE, "bob"); assertAuditReferenceValue(workItemEvents.get(0), WorkflowConstants.AUDIT_TARGET, roleATest1Oid, RoleType.COMPLEX_TYPE, "a-test-1"); // TODO other items List<AuditEventRecord> processEvents = filter(getParamAuditRecords( WorkflowConstants.AUDIT_PROCESS_INSTANCE_ID, wfTask.asObjectable().getWorkflowContext().getProcessInstanceId(), result), AuditEventType.WORKFLOW_PROCESS_INSTANCE, AuditEventStage.EXECUTION); assertAuditReferenceValue(processEvents, WorkflowConstants.AUDIT_OBJECT, userBobOid, UserType.COMPLEX_TYPE, "bob"); assertAuditTarget(processEvents.get(0), userBobOid, UserType.COMPLEX_TYPE, "bob"); assertAuditReferenceValue(processEvents.get(0), WorkflowConstants.AUDIT_TARGET, roleATest1Oid, RoleType.COMPLEX_TYPE, "a-test-1"); // TODO other items } //endregion //region Testing escalation @Test public void test200EscalatedApprovalStart() throws Exception { final String TEST_NAME = "test200EscalatedApprovalStart"; TestUtil.displayTestTile(this, TEST_NAME); Task task = createTask(TestStrings.class.getName() + "." + TEST_NAME); OperationResult result = task.getResult(); dummyAuditService.clear(); dummyTransport.clearMessages(); // WHEN assignRole(userCarlaOid, roleATest1Oid, task, task.getResult()); // THEN assertNotAssignedRole(getUser(userCarlaOid), roleATest1Oid); WorkItemType workItem = getWorkItem(task, result); display("Work item", workItem); PrismObject<TaskType> wfTask = getTask(WfContextUtil.getTask(workItem).getOid()); display("wfTask", wfTask); assertTriggers(wfTask, 2); assertStage(wfTask, 1, 3, "Line managers", null); assertAssignee(workItem, userGuybrushOid, userGuybrushOid); List<Message> lifecycleMessages = dummyTransport.getMessages(DUMMY_WORK_ITEM_LIFECYCLE); List<Message> allocationMessages = dummyTransport.getMessages(DUMMY_WORK_ITEM_ALLOCATION); List<Message> processMessages = dummyTransport.getMessages(DUMMY_PROCESS); display("work items lifecycle notifications", lifecycleMessages); display("work items allocation notifications", allocationMessages); display("processes notifications", processMessages); dummyTransport.clearMessages(); assertEquals("Wrong # of work items messages", 1, lifecycleMessages.size()); assertMessage(lifecycleMessages.get(0), "guybrush@evolveum.com", "A new work item has been created", "Stage: Line managers (1/3)", "Allocated to: Guybrush Threepwood (guybrush)"); assertMessage(allocationMessages.get(0), "guybrush@evolveum.com", "Work item has been allocated to you", "Stage: Line managers (1/3)", "Allocated to: Guybrush Threepwood (guybrush)"); assertEquals("Wrong # of work items allocation messages", 1, allocationMessages.size()); //assertMessage(lifecycleMessages.get(0), "guybrush@evolveum.com", "A new work item has been created", "Stage: Line managers (1/3)", "Guybrush Threepwood (guybrush)"); assertEquals("Wrong # of process messages", 1, processMessages.size()); assertMessage(processMessages.get(0), "administrator@evolveum.com", "Workflow process instance has been started", "Process instance name: Assigning a-test-1 to carla", "Stage: Line managers (1/3)"); display("audit", dummyAuditService); } @Test public void test202FourDaysLater() throws Exception { final String TEST_NAME = "test202FourDaysLater"; TestUtil.displayTestTile(this, TEST_NAME); Task task = createTask(TestStrings.class.getName() + "." + TEST_NAME); OperationResult result = task.getResult(); dummyAuditService.clear(); dummyTransport.clearMessages(); // WHEN clock.overrideDuration("P4D"); waitForTaskNextRun(TASK_TRIGGER_SCANNER_OID, true, 20000, true); // THEN List<Message> lifecycleMessages = dummyTransport.getMessages(DUMMY_WORK_ITEM_LIFECYCLE); List<Message> allocationMessages = dummyTransport.getMessages(DUMMY_WORK_ITEM_ALLOCATION); List<Message> processMessages = dummyTransport.getMessages(DUMMY_PROCESS); display("work items lifecycle notifications", lifecycleMessages); display("work items allocation notifications", allocationMessages); display("processes notifications", processMessages); dummyTransport.clearMessages(); assertNull("lifecycle messages", lifecycleMessages); assertEquals("Wrong # of work items allocation messages", 1, allocationMessages.size()); assertMessage(allocationMessages.get(0), "guybrush@evolveum.com", "Work item will be automatically escalated in 1 day", "Stage: Line managers (1/3)", "Allocated to (before escalation): Guybrush Threepwood (guybrush)"); assertNull("process messages", processMessages); display("audit", dummyAuditService); } // escalation should occur here @Test public void test204SixDaysLater() throws Exception { final String TEST_NAME = "test204SixDaysLater"; TestUtil.displayTestTile(this, TEST_NAME); Task task = createTask(TestStrings.class.getName() + "." + TEST_NAME); OperationResult result = task.getResult(); dummyAuditService.clear(); dummyTransport.clearMessages(); // WHEN clock.resetOverride(); clock.overrideDuration("P6D"); waitForTaskNextRun(TASK_TRIGGER_SCANNER_OID, true, 20000, true); // THEN List<WorkItemType> workItems = getWorkItems(task, result); displayWorkItems("Work items after timed escalation", workItems); assertEquals("Wrong # of work items after timed escalation", 1, workItems.size()); String taskOid = WfContextUtil.getTask(workItems.get(0)).getOid(); PrismObject<TaskType> wfTask = getTask(taskOid); display("wfTask after timed escalation", wfTask); List<Message> lifecycleMessages = dummyTransport.getMessages(DUMMY_WORK_ITEM_LIFECYCLE); List<Message> allocationMessages = dummyTransport.getMessages(DUMMY_WORK_ITEM_ALLOCATION); List<Message> processMessages = dummyTransport.getMessages(DUMMY_PROCESS); display("work items lifecycle notifications", lifecycleMessages); display("work items allocation notifications", allocationMessages); display("processes notifications", processMessages); dummyTransport.clearMessages(); // asserts - work item WorkItemType workItem = workItems.get(0); PrismAsserts.assertReferenceValues(ref(workItem.getAssigneeRef()), userGuybrushOid, userCheeseOid); PrismAsserts.assertDuration("Wrong duration between now and deadline", "P9D", System.currentTimeMillis(), workItem.getDeadline(), null); PrismAsserts.assertReferenceValue(ref(workItem.getOriginalAssigneeRef()), userGuybrushOid); assertEquals("Wrong stage #", (Integer) 1, workItem.getStageNumber()); assertEquals("Wrong escalation level #", 1, WfContextUtil.getEscalationLevelNumber(workItem)); assertEquals("Wrong escalation level name", "Line manager escalation", WfContextUtil.getEscalationLevelName(workItem)); List<CaseEventType> events = assertEvents(wfTask, 2); assertEscalationEvent(events.get(1), userAdministrator.getOid(), userGuybrushOid, 1, "Line managers", Collections.singletonList(userGuybrushOid), Collections.singletonList(userCheeseOid), WorkItemDelegationMethodType.ADD_ASSIGNEES, 1, "Line manager escalation"); // asserts - notifications assertNull("lifecycle messages", lifecycleMessages); assertNull("process messages", processMessages); assertEquals("Wrong # of work items allocation messages", 3, allocationMessages.size()); ArrayListValuedHashMap<String, Message> sorted = sortByRecipients(allocationMessages); assertMessage(sorted.get("guybrush@evolveum.com").get(0), "guybrush@evolveum.com", "Work item has been escalated", "Work item: Approve assigning a-test-1 to carla", "Stage: Line managers (1/3)", "Allocated to (before escalation): Guybrush Threepwood (guybrush)", "(in 5 days)"); assertMessage(sorted.get("guybrush@evolveum.com").get(1), "guybrush@evolveum.com", "Work item has been allocated to you", "Work item: Approve assigning a-test-1 to carla", "Stage: Line managers (1/3)", "Escalation level: Line manager escalation (1)", "|Allocated to (after escalation): Guybrush Threepwood (guybrush), Ignatius Cheese (cheese)|Allocated to (after escalation): Ignatius Cheese (cheese), Guybrush Threepwood (guybrush)", "(in 9 days)"); assertMessage(sorted.get("cheese@evolveum.com").get(0), "cheese@evolveum.com", "Work item has been allocated to you", "Work item: Approve assigning a-test-1 to carla", "Stage: Line managers (1/3)", "Escalation level: Line manager escalation (1)", "|Allocated to (after escalation): Guybrush Threepwood (guybrush), Ignatius Cheese (cheese)|Allocated to (after escalation): Ignatius Cheese (cheese), Guybrush Threepwood (guybrush)", "(in 9 days)"); display("audit", dummyAuditService); } @Test public void test205EightDaysLater() throws Exception { final String TEST_NAME = "test205EightDaysLater"; TestUtil.displayTestTile(this, TEST_NAME); Task task = createTask(TestStrings.class.getName() + "." + TEST_NAME); OperationResult result = task.getResult(); dummyAuditService.clear(); dummyTransport.clearMessages(); // WHEN clock.resetOverride(); clock.overrideDuration("P8D"); waitForTaskNextRun(TASK_TRIGGER_SCANNER_OID, true, 20000, true); // THEN List<Message> lifecycleMessages = dummyTransport.getMessages(DUMMY_WORK_ITEM_LIFECYCLE); List<Message> allocationMessages = dummyTransport.getMessages(DUMMY_WORK_ITEM_ALLOCATION); List<Message> processMessages = dummyTransport.getMessages(DUMMY_PROCESS); display("work items lifecycle notifications", lifecycleMessages); display("work items allocation notifications", allocationMessages); display("processes notifications", processMessages); dummyTransport.clearMessages(); assertNull("lifecycle messages", lifecycleMessages); assertNull("process messages", processMessages); assertEquals("Wrong # of work items allocation messages", 4, allocationMessages.size()); ArrayListValuedHashMap<String, Message> sorted = sortByRecipients(allocationMessages); assertMessage(sorted.get("guybrush@evolveum.com").get(0), "guybrush@evolveum.com", "Work item will be automatically completed in 2 days 12 hours", "Work item: Approve assigning a-test-1 to carla", "Stage: Line managers (1/3)", "Escalation level: Line manager escalation (1)", "|Allocated to: Guybrush Threepwood (guybrush), Ignatius Cheese (cheese)|Allocated to: Ignatius Cheese (cheese), Guybrush Threepwood (guybrush)", "(in 9 days)"); assertMessage(sorted.get("guybrush@evolveum.com").get(1), "guybrush@evolveum.com", "Work item will be automatically completed in 2 days", "Work item: Approve assigning a-test-1 to carla", "Stage: Line managers (1/3)", "Escalation level: Line manager escalation (1)", "|Allocated to: Guybrush Threepwood (guybrush), Ignatius Cheese (cheese)|Allocated to: Ignatius Cheese (cheese), Guybrush Threepwood (guybrush)", "(in 9 days)"); assertMessage(sorted.get("cheese@evolveum.com").get(0), "cheese@evolveum.com", "Work item will be automatically completed in 2 days 12 hours", "Work item: Approve assigning a-test-1 to carla", "Stage: Line managers (1/3)", "Escalation level: Line manager escalation (1)", "|Allocated to: Guybrush Threepwood (guybrush), Ignatius Cheese (cheese)|Allocated to: Ignatius Cheese (cheese), Guybrush Threepwood (guybrush)", "(in 9 days)"); assertMessage(sorted.get("cheese@evolveum.com").get(1), "cheese@evolveum.com", "Work item will be automatically completed in 2 days", "Work item: Approve assigning a-test-1 to carla", "Stage: Line managers (1/3)", "Escalation level: Line manager escalation (1)", "|Allocated to: Guybrush Threepwood (guybrush), Ignatius Cheese (cheese)|Allocated to: Ignatius Cheese (cheese), Guybrush Threepwood (guybrush)", "(in 9 days)"); display("audit", dummyAuditService); } @Test public void test206ApproveByCheese() throws Exception { final String TEST_NAME = "test206ApproveByCheese"; TestUtil.displayTestTile(this, TEST_NAME); Task task = createTask(TestStrings.class.getName() + "." + TEST_NAME); OperationResult result = task.getResult(); dummyAuditService.clear(); dummyTransport.clearMessages(); // GIVEN login(userAdministrator); clock.resetOverride(); WorkItemType workItem = getWorkItem(task, result); PrismObject<UserType> cheese = getUserFromRepo(userCheeseOid); login(cheese); // WHEN workflowService.completeWorkItem(workItem.getExternalId(), true, "OK. Cheese.", null, result); // THEN login(userAdministrator); List<WorkItemType> workItems = getWorkItems(task, result); assertEquals("Wrong # of work items on level 2", 2, workItems.size()); displayWorkItems("Work item after 1st approval", workItems); PrismObject<TaskType> wfTask = getTask(WfContextUtil.getTask(workItem).getOid()); display("wfTask after 1st approval", wfTask); assertStage(wfTask, 2, 3, "Security", null); assertTriggers(wfTask, 4); List<Message> lifecycleMessages = dummyTransport.getMessages(DUMMY_WORK_ITEM_LIFECYCLE); List<Message> allocationMessages = dummyTransport.getMessages(DUMMY_WORK_ITEM_ALLOCATION); List<Message> processMessages = dummyTransport.getMessages(DUMMY_PROCESS); display("work items lifecycle notifications", lifecycleMessages); display("work items allocation notifications", allocationMessages); display("processes notifications", processMessages); dummyTransport.clearMessages(); assertEquals("Wrong # of work items lifecycle messages", 4, lifecycleMessages.size()); assertEquals("Wrong # of work items allocation messages", 4, allocationMessages.size()); assertNull("process messages", processMessages); Map<String,Message> sorted = sortByRecipientsSingle(lifecycleMessages); assertMessage(sorted.get("guybrush@evolveum.com"), "guybrush@evolveum.com", "Work item has been completed", "Work item: Approve assigning a-test-1 to carla", "Stage: Line managers (1/3)", "Escalation level: Line manager escalation (1)", "Originally allocated to: Guybrush Threepwood (guybrush)", "|Allocated to: Guybrush Threepwood (guybrush), Ignatius Cheese (cheese)|Allocated to: Ignatius Cheese (cheese), Guybrush Threepwood (guybrush)", "Carried out by: Ignatius Cheese (cheese)", "Result: APPROVED", "^Deadline:"); assertMessage(sorted.get("cheese@evolveum.com"), "cheese@evolveum.com", "Work item has been completed", "Work item: Approve assigning a-test-1 to carla", "Stage: Line managers (1/3)", "Escalation level: Line manager escalation (1)", "Originally allocated to: Guybrush Threepwood (guybrush)", "|Allocated to: Guybrush Threepwood (guybrush), Ignatius Cheese (cheese)|Allocated to: Ignatius Cheese (cheese), Guybrush Threepwood (guybrush)", "Carried out by: Ignatius Cheese (cheese)", "Result: APPROVED", "^Deadline:"); assertMessage(sorted.get("elaine@evolveum.com"), "elaine@evolveum.com", "A new work item has been created", "Work item: Approve assigning a-test-1 to carla", "Stage: Security (2/3)", "Allocated to: Elaine Marley (elaine)", "(in 7 days)", "^Result:"); assertMessage(sorted.get("barkeeper@evolveum.com"), "barkeeper@evolveum.com", "A new work item has been created", "Work item: Approve assigning a-test-1 to carla", "Stage: Security (2/3)", "Allocated to: Horridly Scarred Barkeep (barkeeper)", "(in 7 days)", "^Result:"); Map<String,Message> sorted2 = sortByRecipientsSingle(allocationMessages); assertMessage(sorted2.get("guybrush@evolveum.com"), "guybrush@evolveum.com", "Work item has been completed", "Work item: Approve assigning a-test-1 to carla", "Stage: Line managers (1/3)", "Escalation level: Line manager escalation (1)", "Originally allocated to: Guybrush Threepwood (guybrush)", "|Allocated to: Guybrush Threepwood (guybrush), Ignatius Cheese (cheese)|Allocated to: Ignatius Cheese (cheese), Guybrush Threepwood (guybrush)", "Carried out by: Ignatius Cheese (cheese)", "Result: APPROVED", "^Deadline:"); assertMessage(sorted2.get("cheese@evolveum.com"), "cheese@evolveum.com", "Work item has been completed", "Work item: Approve assigning a-test-1 to carla", "Stage: Line managers (1/3)", "Escalation level: Line manager escalation (1)", "Originally allocated to: Guybrush Threepwood (guybrush)", "|Allocated to: Guybrush Threepwood (guybrush), Ignatius Cheese (cheese)|Allocated to: Ignatius Cheese (cheese), Guybrush Threepwood (guybrush)", "Carried out by: Ignatius Cheese (cheese)", "Result: APPROVED", "^Deadline:"); assertMessage(sorted2.get("elaine@evolveum.com"), "elaine@evolveum.com", "Work item has been allocated to you", "Work item: Approve assigning a-test-1 to carla", "Stage: Security (2/3)", "Allocated to: Elaine Marley (elaine)", "(in 7 days)", "^Result:"); assertMessage(sorted2.get("barkeeper@evolveum.com"), "barkeeper@evolveum.com", "Work item has been allocated to you", "Work item: Approve assigning a-test-1 to carla", "Stage: Security (2/3)", "Allocated to: Horridly Scarred Barkeep (barkeeper)", "(in 7 days)", "^Result:"); display("audit", dummyAuditService); } // notification should be send @Test public void test208SixDaysLater() throws Exception { final String TEST_NAME = "test208SixDaysLater"; TestUtil.displayTestTile(this, TEST_NAME); Task task = createTask(TestStrings.class.getName() + "." + TEST_NAME); OperationResult result = task.getResult(); dummyAuditService.clear(); dummyTransport.clearMessages(); // GIVEN clock.resetOverride(); resetTriggerTask(TASK_TRIGGER_SCANNER_OID, TASK_TRIGGER_SCANNER_FILE, result); clock.overrideDuration("P6D"); // WHEN waitForTaskNextRun(TASK_TRIGGER_SCANNER_OID, true, 20000, true); // THEN List<Message> lifecycleMessages = dummyTransport.getMessages(DUMMY_WORK_ITEM_LIFECYCLE); List<Message> allocationMessages = dummyTransport.getMessages(DUMMY_WORK_ITEM_ALLOCATION); List<Message> processMessages = dummyTransport.getMessages(DUMMY_PROCESS); display("work items lifecycle notifications", lifecycleMessages); display("work items allocation notifications", allocationMessages); display("processes notifications", processMessages); dummyTransport.clearMessages(); assertNull("lifecycle messages", lifecycleMessages); assertNull("process messages", processMessages); assertEquals("Wrong # of work items allocation messages", 2, allocationMessages.size()); Map<String, Message> sorted = sortByRecipientsSingle(allocationMessages); assertMessage(sorted.get("elaine@evolveum.com"), "elaine@evolveum.com", "Work item will be automatically completed in 2 days", "Security (2/3)", "Allocated to: Elaine Marley (elaine)", "(in 7 days)"); assertMessage(sorted.get("barkeeper@evolveum.com"), "barkeeper@evolveum.com", "Work item will be automatically completed in 2 days", "Security (2/3)", "Allocated to: Horridly Scarred Barkeep (barkeeper)", "(in 7 days)"); display("audit", dummyAuditService); } @Test public void test209EightDaysLater() throws Exception { final String TEST_NAME = "test209EightDaysLater"; TestUtil.displayTestTile(this, TEST_NAME); Task task = createTask(TestStrings.class.getName() + "." + TEST_NAME); OperationResult result = task.getResult(); dummyAuditService.clear(); dummyTransport.clearMessages(); // GIVEN clock.resetOverride(); clock.overrideDuration("P8D"); // WHEN waitForTaskNextRun(TASK_TRIGGER_SCANNER_OID, true, 20000, true); // THEN List<Message> lifecycleMessages = dummyTransport.getMessages(DUMMY_WORK_ITEM_LIFECYCLE); List<Message> allocationMessages = dummyTransport.getMessages(DUMMY_WORK_ITEM_ALLOCATION); List<Message> processMessages = dummyTransport.getMessages(DUMMY_PROCESS); display("work items lifecycle notifications", lifecycleMessages); display("work items allocation notifications", allocationMessages); display("processes notifications", processMessages); dummyTransport.clearMessages(); assertEquals("Wrong # of work items lifecycle messages", 2, lifecycleMessages.size()); assertEquals("Wrong # of work items allocation messages", 2, allocationMessages.size()); assertEquals("Wrong # of process messages", 1, processMessages.size()); checkTwoCompleted(lifecycleMessages); checkTwoCompleted(allocationMessages); assertMessage(processMessages.get(0), "administrator@evolveum.com", "Workflow process instance has finished", "Process instance name: Assigning a-test-1 to carla", "Result: REJECTED"); display("audit", dummyAuditService); } private void checkTwoCompleted(List<Message> lifecycleMessages) { Map<String, Message> sorted = sortByRecipientsSingle(lifecycleMessages); assertMessage(sorted.get("elaine@evolveum.com"), "elaine@evolveum.com", null, "Security (2/3)", "Allocated to: Elaine Marley (elaine)"); assertMessage(sorted.get("barkeeper@evolveum.com"), "barkeeper@evolveum.com", null, "Security (2/3)", "Allocated to: Horridly Scarred Barkeep (barkeeper)"); int completed; assertMessage(lifecycleMessages.get(0), null, "Work item has been completed", "^Carried out by:", "Reason: Timed action", "Result: REJECTED"); assertMessage(lifecycleMessages.get(1), null, "Work item has been completed", "^Carried out by:", "Reason: Timed action", "Result: REJECTED"); } @Test public void test220FormRoleAssignmentStart() throws Exception { final String TEST_NAME = "test220FormRoleAssignmentStart"; TestUtil.displayTestTile(this, TEST_NAME); Task task = createTask(TestStrings.class.getName() + "." + TEST_NAME); OperationResult result = task.getResult(); dummyAuditService.clear(); dummyTransport.clearMessages(); // WHEN assignRole(userBobOid, roleATest4Oid, task, task.getResult()); // THEN assertNotAssignedRole(getUser(userBobOid), roleATest4Oid); List<WorkItemType> workItems = getWorkItems(task, result); displayWorkItems("Work item after start", workItems); PrismObject<TaskType> wfTask = getTask(WfContextUtil.getTask(workItems.get(0)).getOid()); display("wfTask", wfTask); // assertTriggers(wfTask, 2); ItemApprovalProcessStateType info = WfContextUtil.getItemApprovalProcessInfo(wfTask.asObjectable().getWorkflowContext()); ApprovalSchemaType schema = info.getApprovalSchema(); assertEquals("Wrong # of approval levels", 2, schema.getStage().size()); assertApprovalLevel(schema, 1, "Line managers", "P5D", 2); assertApprovalLevel(schema, 2, "Role approvers (first)", "P5D", 2); assertStage(wfTask, 1, 2, "Line managers", null); List<Message> lifecycleMessages = dummyTransport.getMessages(DUMMY_WORK_ITEM_LIFECYCLE); List<Message> allocationMessages = dummyTransport.getMessages(DUMMY_WORK_ITEM_ALLOCATION); List<Message> processMessages = dummyTransport.getMessages(DUMMY_PROCESS); display("work items lifecycle notifications", lifecycleMessages); display("work items allocation notifications", allocationMessages); display("processes notifications", processMessages); display("audit", dummyAuditService); } @Test public void test221FormApproveByLechuck() throws Exception { final String TEST_NAME = "test221FormApproveByLechuck"; TestUtil.displayTestTile(this, TEST_NAME); Task task = createTask(TestStrings.class.getName() + "." + TEST_NAME); OperationResult result = task.getResult(); dummyAuditService.clear(); dummyTransport.clearMessages(); // GIVEN login(userAdministrator); WorkItemType workItem = getWorkItem(task, result); // WHEN PrismObject<UserType> lechuck = getUserFromRepo(userLechuckOid); login(lechuck); workflowService.completeWorkItem(workItem.getExternalId(), true, "OK. LeChuck", null, result); // THEN login(userAdministrator); List<WorkItemType> workItems = getWorkItems(task, result); displayWorkItems("Work item after 1st approval", workItems); PrismObject<TaskType> wfTask = getTask(WfContextUtil.getTask(workItems.get(0)).getOid()); assertStage(wfTask, 2, 2, "Role approvers (first)", null); ApprovalStageDefinitionType level = WfContextUtil.getCurrentStageDefinition(wfTask.asObjectable().getWorkflowContext()); assertEquals("Wrong evaluation strategy", LevelEvaluationStrategyType.FIRST_DECIDES, level.getEvaluationStrategy()); // notifications List<Message> lifecycleMessages = dummyTransport.getMessages(DUMMY_WORK_ITEM_LIFECYCLE); List<Message> allocationMessages = dummyTransport.getMessages(DUMMY_WORK_ITEM_ALLOCATION); List<Message> processMessages = dummyTransport.getMessages(DUMMY_PROCESS); display("work items lifecycle notifications", lifecycleMessages); display("work items allocation notifications", allocationMessages); display("processes notifications", processMessages); dummyTransport.clearMessages(); display("audit", dummyAuditService); } @Test public void test222FormApproveByCheese() throws Exception { final String TEST_NAME = "test222FormApproveByCheese"; TestUtil.displayTestTile(this, TEST_NAME); Task task = createTask(TestStrings.class.getName() + "." + TEST_NAME); OperationResult result = task.getResult(); dummyAuditService.clear(); dummyTransport.clearMessages(); // GIVEN login(userAdministrator); SearchResultList<WorkItemType> workItems = getWorkItems(task, result); WorkItemType workItem = sortByOriginalAssignee(workItems).get(userCheeseOid); assertNotNull("No work item for cheese", workItem); // WHEN PrismObject<UserType> cheese = getUserFromRepo(userCheeseOid); login(cheese); ObjectDelta formDelta = DeltaBuilder.deltaFor(UserType.class, prismContext) .item(UserType.F_DESCRIPTION).replace("Hello") .asObjectDelta(userBobOid); workflowService.completeWorkItem(workItem.getExternalId(), true, "OK. LeChuck", formDelta, result); // THEN login(userAdministrator); workItems = getWorkItems(task, result); displayWorkItems("Work item after 2nd approval", workItems); assertEquals("Wrong # of work items after 2nd approval", 0, workItems.size()); PrismObject<TaskType> wfTask = getTask(WfContextUtil.getTask(workItem).getOid()); display("wfTask after 2nd approval", wfTask); assertStage(wfTask, 2, 2, "Role approvers (first)", null); // assertTriggers(wfTask, 4); // notifications List<Message> lifecycleMessages = dummyTransport.getMessages(DUMMY_WORK_ITEM_LIFECYCLE); List<Message> allocationMessages = dummyTransport.getMessages(DUMMY_WORK_ITEM_ALLOCATION); List<Message> processMessages = dummyTransport.getMessages(DUMMY_PROCESS); display("work items lifecycle notifications", lifecycleMessages); display("work items allocation notifications", allocationMessages); display("processes notifications", processMessages); // audit display("audit", dummyAuditService); List<AuditEventRecord> records = dummyAuditService.getRecords(); assertEquals("Wrong # of audit records", 4, records.size()); AuditEventRecord record = records.get(0); Collection<ObjectDeltaOperation<? extends ObjectType>> deltas = record.getDeltas(); assertEquals("Wrong # of deltas in audit record", 1, deltas.size()); ObjectDeltaOperation<? extends ObjectType> delta = deltas.iterator().next(); assertEquals("Wrong # of modifications in audit record delta", 1, delta.getObjectDelta().getModifications().size()); ItemDelta<?, ?> itemDelta = delta.getObjectDelta().getModifications().iterator().next(); if (!new ItemPath(UserType.F_DESCRIPTION).equivalent(itemDelta.getPath())) { fail("Wrong item path in delta: expected: "+new ItemPath(UserType.F_DESCRIPTION)+", found: "+itemDelta.getPath()); } assertEquals("Wrong value in delta", "Hello", itemDelta.getValuesToReplace().iterator().next().getRealValue()); // record #1, #2: cancellation of work items of other approvers // record #3: finishing process execution } @Test public void test250ApproverAssignment() throws Exception { final String TEST_NAME = "test250ApproverAssignment"; TestUtil.displayTestTile(this, TEST_NAME); Task task = createTask(TestStrings.class.getName() + "." + TEST_NAME); OperationResult result = task.getResult(); dummyAuditService.clear(); dummyTransport.clearMessages(); // WHEN assignRole(userBobOid, roleATest1Oid, SchemaConstants.ORG_APPROVER, task, task.getResult()); // THEN assertNull("bob has assigned a-test-1 as an approver", getUserAssignment(userBobOid, roleATest1Oid, SchemaConstants.ORG_APPROVER)); List<WorkItemType> workItems = getWorkItems(task, result); displayWorkItems("Work item after start", workItems); PrismObject<TaskType> wfTask = getTask(WfContextUtil.getTask(workItems.get(0)).getOid()); display("wfTask", wfTask); ItemApprovalProcessStateType info = WfContextUtil.getItemApprovalProcessInfo(wfTask.asObjectable().getWorkflowContext()); ApprovalSchemaType schema = info.getApprovalSchema(); assertEquals("Wrong # of approval levels", 3, schema.getStage().size()); assertApprovalLevel(schema, 1, "Line managers", "P5D", 2); assertApprovalLevel(schema, 2, "Security", "P7D", 1); assertApprovalLevel(schema, 3, "Role approvers (all)", "P5D", 2); assertStage(wfTask, 1, 3, "Line managers", null); List<Message> lifecycleMessages = dummyTransport.getMessages(DUMMY_WORK_ITEM_LIFECYCLE); List<Message> allocationMessages = dummyTransport.getMessages(DUMMY_WORK_ITEM_ALLOCATION); List<Message> processMessages = dummyTransport.getMessages(DUMMY_PROCESS); display("work items lifecycle notifications", lifecycleMessages); display("work items allocation notifications", allocationMessages); display("processes notifications", processMessages); display("audit", dummyAuditService); } //endregion //region TODO deduplicate with AbstractWfTestPolicy private void displayWorkItems(String title, List<WorkItemType> workItems) { workItems.forEach(wi -> display(title, wi)); } protected WorkItemType getWorkItem(Task task, OperationResult result) throws Exception { SearchResultList<WorkItemType> itemsAll = getWorkItems(task, result); if (itemsAll.size() != 1) { System.out.println("Unexpected # of work items: " + itemsAll.size()); for (WorkItemType workItem : itemsAll) { System.out.println(PrismUtil.serializeQuietly(prismContext, workItem)); } } assertEquals("Wrong # of total work items", 1, itemsAll.size()); return itemsAll.get(0); } private SearchResultList<WorkItemType> getWorkItems(Task task, OperationResult result) throws Exception { return modelService.searchContainers(WorkItemType.class, null, null, task, result); } protected ObjectReferenceType ort(String oid) { return ObjectTypeUtil.createObjectRef(oid, ObjectTypes.USER); } protected PrismReferenceValue prv(String oid) { return ObjectTypeUtil.createObjectRef(oid, ObjectTypes.USER).asReferenceValue(); } protected PrismReference ref(List<ObjectReferenceType> orts) { PrismReference rv = new PrismReference(new QName("dummy")); orts.forEach(ort -> rv.add(ort.asReferenceValue().clone())); return rv; } protected PrismReference ref(ObjectReferenceType ort) { return ref(Collections.singletonList(ort)); } protected Map<String, WorkItemType> sortByOriginalAssignee(Collection<WorkItemType> workItems) { Map<String, WorkItemType> rv = new HashMap<>(); workItems.forEach(wi -> rv.put(wi.getOriginalAssigneeRef().getOid(), wi)); return rv; } //endregion private void assertMessage(Message message, String recipient, String subject, String... texts) { assertNotNull("No message for " + recipient, message); assertEquals("Wrong # of recipients", 1, message.getTo().size()); if (recipient != null) { assertEquals("Wrong recipient", recipient, message.getTo().get(0)); } if (subject != null) { assertEquals("Wrong subject", subject, message.getSubject()); } condition: for (String text : texts) { if (text.startsWith("^")) { String pureText = text.substring(1); if (message.getBody().contains(pureText)) { fail("Message body does contain '" + pureText + "' even if it shouldn't: " + message.getBody()); } } else if (text.startsWith("|")) { String[] strings = StringUtils.split(text, "|"); for (String string : strings) { if (message.getBody().contains(string)) { continue condition; } } fail("Message body does not contain any of " + Arrays.asList(strings) + ": " + message.getBody()); } else { if (!message.getBody().contains(text)) { fail("Message body doesn't contain '" + text + "': " + message.getBody()); } } } } private ArrayListValuedHashMap<String, Message> sortByRecipients(Collection<Message> messages) { ArrayListValuedHashMap<String, Message> rv = new ArrayListValuedHashMap<>(); messages.forEach(m -> m.getTo().forEach( to -> rv.put(to, m))); return rv; } private Map<String, Message> sortByRecipientsSingle(Collection<Message> messages) { Map<String, Message> rv = new HashMap<>(); messages.forEach(m -> m.getTo().forEach( to -> rv.put(to, m))); return rv; } private Task getParentTask(PrismObject<TaskType> task, OperationResult result) throws SchemaException, ObjectNotFoundException { return taskManager.getTaskByIdentifier(task.asObjectable().getParent(), result); } private void assertTriggers(PrismObject<TaskType> wfTask, int count) { assertEquals("Wrong # of triggers", count, wfTask.asObjectable().getTrigger().size()); } private void assertAssignee(WorkItemType workItem, String originalAssignee, String... currentAssignee) { assertRefEquals("Wrong original assignee", ObjectTypeUtil.createObjectRef(originalAssignee, ObjectTypes.USER), workItem.getOriginalAssigneeRef()); assertReferenceValues(ref(workItem.getAssigneeRef()), currentAssignee); } private void assertStage(PrismObject<TaskType> wfTask, Integer stageNumber, Integer stageCount, String stageName, String stageDisplayName) { WfContextType wfc = wfTask.asObjectable().getWorkflowContext(); assertEquals("Wrong stage number", stageNumber, wfc.getStageNumber()); assertEquals("Wrong stage count", stageCount, WfContextUtil.getStageCount(wfc)); assertEquals("Wrong stage name", stageName, WfContextUtil.getStageName(wfc)); assertEquals("Wrong stage name", stageDisplayName, WfContextUtil.getStageDisplayName(wfc)); } private void assertApprovalLevel(ApprovalSchemaType schema, int number, String name, String duration, int timedActions) { ApprovalStageDefinitionType level = schema.getStage().get(number-1); assertEquals("Wrong level number", number, (int) level.getNumber()); assertEquals("Wrong level name", name, level.getName()); assertEquals("Wrong level duration", XmlTypeConverter.createDuration(duration), level.getDuration()); assertEquals("Wrong # of timed actions", timedActions, level.getTimedActions().size()); } private List<CaseEventType> assertEvents(PrismObject<TaskType> wfTask, int expectedCount) { WfContextType wfc = wfTask.asObjectable().getWorkflowContext(); assertEquals("Wrong # of wf events", expectedCount, wfc.getEvent().size()); return wfc.getEvent(); } private void assertEscalationEvent(CaseEventType wfProcessEventType, String initiator, String originalAssignee, int stageNumber, String stageName, List<String> assigneesBefore, List<String> delegatedTo, WorkItemDelegationMethodType methodType, int newEscalationLevelNumber, String newEscalationLevelName) { if (!(wfProcessEventType instanceof WorkItemEscalationEventType)) { fail("Wrong event class: expected: " + WorkItemEscalationEventType.class + ", real: " + wfProcessEventType.getClass()); } WorkItemEscalationEventType event = (WorkItemEscalationEventType) wfProcessEventType; assertEvent(event, initiator, originalAssignee, stageNumber, stageName); PrismAsserts.assertReferenceValues(ref(event.getAssigneeBefore()), assigneesBefore.toArray(new String[0])); PrismAsserts.assertReferenceValues(ref(event.getDelegatedTo()), delegatedTo.toArray(new String[0])); assertEquals("Wrong delegation method", methodType, event.getDelegationMethod()); assertEquals("Wrong escalation level #", (Integer) newEscalationLevelNumber, event.getNewEscalationLevel().getNumber()); assertEquals("Wrong escalation level name", newEscalationLevelName, event.getNewEscalationLevel().getName()); } private void assertCompletionEvent(CaseEventType wfProcessEventType, String initiator, String originalAssignee, int stageNumber, String stageName, WorkItemOutcomeType outcome, String comment) { if (!(wfProcessEventType instanceof WorkItemCompletionEventType)) { fail("Wrong event class: expected: " + WorkItemCompletionEventType.class + ", real: " + wfProcessEventType.getClass()); } WorkItemCompletionEventType event = (WorkItemCompletionEventType) wfProcessEventType; assertEvent(event, initiator, originalAssignee, stageNumber, stageName); assertEquals("Wrong outcome", outcome, ApprovalUtils.fromUri(event.getOutput().getOutcome())); assertEquals("Wrong comment", comment, event.getOutput().getComment()); } private void assertEvent(CaseEventType processEvent, String initiator, String originalAssignee, Integer stageNumber, String stageName) { if (!(processEvent instanceof WorkItemEventType)) { fail("Wrong event class: expected: " + WorkItemEventType.class + ", real: " + processEvent.getClass()); } WorkItemEventType event = (WorkItemEventType) processEvent; PrismAsserts.assertReferenceValue(ref(event.getInitiatorRef()), initiator); assertEquals("Wrong stage #", stageNumber, event.getStageNumber()); //assertEquals("Wrong stage name", stageName, event.getStageName()); if (originalAssignee != null) { assertNotNull("Null original assignee", event.getOriginalAssigneeRef()); PrismAsserts.assertReferenceValue(ref(event.getOriginalAssigneeRef()), originalAssignee); } } }