/*
* 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.wf.impl.policy.other;
import com.evolveum.midpoint.audit.api.AuditEventRecord;
import com.evolveum.midpoint.audit.api.AuditEventType;
import com.evolveum.midpoint.model.api.WorkflowService;
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.util.PrismAsserts;
import com.evolveum.midpoint.schema.SearchResultList;
import com.evolveum.midpoint.schema.result.OperationResult;
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.wf.api.WorkflowConstants;
import com.evolveum.midpoint.wf.impl.activiti.ActivitiEngine;
import com.evolveum.midpoint.wf.impl.policy.AbstractWfTestPolicy;
import com.evolveum.midpoint.wf.util.ApprovalUtils;
import com.evolveum.midpoint.xml.ns._public.common.common_3.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.testng.annotations.Test;
import java.io.File;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static com.evolveum.midpoint.test.IntegrationTestTools.display;
import static com.evolveum.midpoint.test.IntegrationTestTools.displayCollection;
import static org.testng.AssertJUnit.*;
/**
* @author mederly
*/
@ContextConfiguration(locations = {"classpath:ctx-workflow-test-main.xml"})
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
public class TestEscalation extends AbstractWfTestPolicy {
@Override
protected PrismObject<UserType> getDefaultActor() {
return userAdministrator;
}
@Autowired
private ActivitiEngine activitiEngine;
@Autowired
private WorkflowService workflowService;
protected static final File TASK_TRIGGER_SCANNER_FILE = new File(COMMON_DIR, "task-trigger-scanner.xml");
protected static final String TASK_TRIGGER_SCANNER_OID = "00000000-0000-0000-0000-000000000007";
protected static final File TEST_ESCALATION_RESOURCE_DIR = new File("src/test/resources/policy/escalation");
protected static final File METAROLE_ESCALATED_FILE = new File(TEST_ESCALATION_RESOURCE_DIR, "metarole-escalated.xml");
protected static final File ROLE_E1_FILE = new File(TEST_ESCALATION_RESOURCE_DIR, "role-e1.xml");
protected static final File ROLE_E2_FILE = new File(TEST_ESCALATION_RESOURCE_DIR, "role-e2.xml");
protected String metaroleEscalatedOid;
protected String roleE1Oid;
protected String roleE2Oid;
private PrismObject<UserType> userLead1, userLead2;
private String workItemId;
private String approvalTaskOid;
@Override
public void initSystem(Task initTask, OperationResult initResult) throws Exception {
super.initSystem(initTask, initResult);
DebugUtil.setPrettyPrintBeansAs(PrismContext.LANG_YAML);
metaroleEscalatedOid = repoAddObjectFromFile(METAROLE_ESCALATED_FILE, initResult).getOid();
roleE1Oid = repoAddObjectFromFile(ROLE_E1_FILE, initResult).getOid();
roleE2Oid = repoAddObjectFromFile(ROLE_E2_FILE, initResult).getOid();
importObjectFromFile(TASK_TRIGGER_SCANNER_FILE);
userLead1 = getUser(userLead1Oid);
userLead2 = getUser(userLead2Oid);
importLead1Deputies(initTask, initResult);
}
@Test
public void test100CreateTask() throws Exception {
final String TEST_NAME = "test100CreateTask";
TestUtil.displayTestTile(this, TEST_NAME);
login(userAdministrator);
Task task = createTask(TEST_NAME);
OperationResult result = task.getResult();
assignRole(userJackOid, roleE1Oid, task, result); // should start approval process
assertNotAssignedRole(userJackOid, roleE1Oid, task, result);
WorkItemType workItem = getWorkItem(task, result);
workItemId = workItem.getExternalId();
approvalTaskOid = WfContextUtil.getTask(workItem).getOid();
PrismObject<TaskType> wfTask = getTask(approvalTaskOid);
display("work item", workItem);
display("workflow task", wfTask);
// 5 days: notification
// D-2 days: escalate
// D-0 days: approve
assertEquals("Wrong # of triggers", 3, wfTask.asObjectable().getTrigger().size());
PrismAsserts.assertReferenceValues(ref(workItem.getAssigneeRef()), userLead1Oid);
PrismAsserts.assertReferenceValue(ref(workItem.getOriginalAssigneeRef()), userLead1Oid);
}
@Test
public void test110Notify() throws Exception {
final String TEST_NAME = "test110Notify";
TestUtil.displayTestTile(this, TEST_NAME);
login(userAdministrator);
Task task = createTask(TEST_NAME);
OperationResult result = task.getResult();
clock.overrideDuration("P6D"); // at P5D there's a notify action
waitForTaskNextRun(TASK_TRIGGER_SCANNER_OID, true, 20000, true);
// TODO assert notifications
WorkItemType workItem = getWorkItem(task, result);
display("work item", workItem);
String wfTaskOid = WfContextUtil.getTask(workItem).getOid();
PrismObject<TaskType> wfTask = getTask(wfTaskOid);
display("task", wfTask);
assertEquals("Wrong # of triggers", 2, wfTask.asObjectable().getTrigger().size());
PrismAsserts.assertReferenceValues(ref(workItem.getAssigneeRef()), userLead1Oid);
PrismAsserts.assertReferenceValue(ref(workItem.getOriginalAssigneeRef()), userLead1Oid);
}
@Test
public void test120Escalate() throws Exception {
final String TEST_NAME = "test120Escalate";
TestUtil.displayTestTile(this, TEST_NAME);
login(userAdministrator);
Task task = createTask(TEST_NAME);
OperationResult result = task.getResult();
clock.resetOverride();
clock.overrideDuration("P13D"); // at -P2D (i.e. P12D) there is a delegate action
waitForTaskNextRun(TASK_TRIGGER_SCANNER_OID, true, 20000, true);
WorkItemType workItem = getWorkItem(task, result);
display("work item", workItem);
PrismObject<TaskType> wfTask = getTask(WfContextUtil.getTask(workItem).getOid());
display("task", wfTask);
assertEquals("Wrong # of triggers", 1, wfTask.asObjectable().getTrigger().size());
PrismAsserts.assertReferenceValues(ref(workItem.getAssigneeRef()), userLead1Oid, userLead2Oid);
PrismAsserts.assertReferenceValue(ref(workItem.getOriginalAssigneeRef()), userLead1Oid);
assertEquals("Wrong escalation level number", 1, WfContextUtil.getEscalationLevelNumber(workItem));
}
@Test
public void test130Complete() throws Exception {
final String TEST_NAME = "test130Complete";
TestUtil.displayTestTile(this, TEST_NAME);
login(userAdministrator);
Task task = createTask(TEST_NAME);
OperationResult result = task.getResult();
clock.resetOverride();
clock.overrideDuration("P15D"); // at 0 (i.e. P14D) there is a delegate action
waitForTaskNextRun(TASK_TRIGGER_SCANNER_OID, true, 20000, true);
PrismObject<TaskType> wfTask = getTask(approvalTaskOid);
display("task", wfTask);
assertEquals("Wrong # of triggers", 0, wfTask.asObjectable().getTrigger().size());
Task rootTask = taskManager.getTaskByIdentifier(wfTask.asObjectable().getParent(), result);
display("rootTask", rootTask);
waitForTaskClose(rootTask, 60000);
assertAssignedRole(userJackOid, roleE1Oid, task, result);
}
@Test
public void test200CreateTaskE2() throws Exception {
final String TEST_NAME = "test200CreateTaskE2";
TestUtil.displayTestTile(this, TEST_NAME);
login(userAdministrator);
Task task = createTask(TEST_NAME);
OperationResult result = task.getResult();
clock.resetOverride();
resetTriggerTask(TASK_TRIGGER_SCANNER_OID, TASK_TRIGGER_SCANNER_FILE, result);
// WHEN
assignRole(userJackOid, roleE2Oid, task, result); // should start approval process
// THEN
assertNotAssignedRole(userJackOid, roleE2Oid, task, result);
List<WorkItemType> workItems = getWorkItems(task, result);
displayWorkItems("Work items", workItems);
approvalTaskOid = WfContextUtil.getTask(workItems.get(0)).getOid();
PrismObject<TaskType> wfTask = getTask(approvalTaskOid);
display("workflow task", wfTask);
// D-0 days: escalate (twice)
assertEquals("Wrong # of triggers", 2, wfTask.asObjectable().getTrigger().size());
}
@Test
public void test210Escalate() throws Exception {
final String TEST_NAME = "test210Escalate";
TestUtil.displayTestTile(this, TEST_NAME);
login(userAdministrator);
Task task = createTask(TEST_NAME);
OperationResult result = task.getResult();
dummyAuditService.clear();
dummyTransport.clearMessages();
// WHEN
clock.overrideDuration("P3DT10M"); // at 3D there's a deadline with escalation
waitForTaskNextRun(TASK_TRIGGER_SCANNER_OID, true, 20000, true);
// THEN
SearchResultList<WorkItemType> workItems = getWorkItems(task, result);
displayWorkItems("Work items after deadline", workItems);
PrismObject<TaskType> wfTask = getTask(approvalTaskOid);
display("workflow task", wfTask);
// D-0 days: reject (twice)
assertEquals("Wrong # of triggers", 2, wfTask.asObjectable().getTrigger().size());
displayCollection("audit records", dummyAuditService.getRecords());
display("dummy transport", dummyTransport);
}
@Test
public void test220Reject() throws Exception {
final String TEST_NAME = "test220Reject";
TestUtil.displayTestTile(this, TEST_NAME);
login(userAdministrator);
Task task = createTask(TEST_NAME);
OperationResult result = task.getResult();
dummyAuditService.clear();
dummyTransport.clearMessages();
// WHEN
clock.resetOverride();
clock.overrideDuration("P5DT20M"); // at 5D there's a deadline with auto-rejection
waitForTaskNextRun(TASK_TRIGGER_SCANNER_OID, true, 20000, true);
// THEN
SearchResultList<WorkItemType> workItems = getWorkItems(task, result);
displayWorkItems("Work items after deadline", workItems);
assertEquals("Wrong # of work items", 0, workItems.size());
PrismObject<TaskType> wfTask = getTask(approvalTaskOid);
display("workflow task", wfTask);
assertEquals("Wrong # of triggers", 0, wfTask.asObjectable().getTrigger().size());
Map<String, WorkItemCompletionEventType> eventMap = new HashMap<>();
for (CaseEventType event : wfTask.asObjectable().getWorkflowContext().getEvent()) {
if (event instanceof WorkItemCompletionEventType) {
WorkItemCompletionEventType c = (WorkItemCompletionEventType) event;
eventMap.put(c.getExternalWorkItemId(), c);
assertNotNull("No result in "+c, c.getOutput());
assertEquals("Wrong outcome in "+c, WorkItemOutcomeType.REJECT, ApprovalUtils.fromUri(c.getOutput().getOutcome()));
assertNotNull("No cause in "+c, c.getCause());
assertEquals("Wrong cause type in "+c, WorkItemEventCauseTypeType.TIMED_ACTION, c.getCause().getType());
assertEquals("Wrong cause name in "+c, "auto-reject", c.getCause().getName());
assertEquals("Wrong cause display name in "+c, "Automatic rejection at deadline", c.getCause().getDisplayName());
}
}
assertEquals("Wrong # of completion events", 2, eventMap.size());
displayCollection("audit records", dummyAuditService.getRecords());
List<AuditEventRecord> workItemAuditRecords = dummyAuditService.getRecordsOfType(AuditEventType.WORK_ITEM);
assertEquals("Wrong # of work item audit records", 2, workItemAuditRecords.size());
for (AuditEventRecord r : workItemAuditRecords) {
assertEquals("Wrong causeType in "+r, Collections.singleton("timedAction"), r.getPropertyValues(WorkflowConstants.AUDIT_CAUSE_TYPE));
assertEquals("Wrong causeName in "+r, Collections.singleton("auto-reject"), r.getPropertyValues(WorkflowConstants.AUDIT_CAUSE_NAME));
assertEquals("Wrong causeDisplayName in "+r, Collections.singleton("Automatic rejection at deadline"), r.getPropertyValues(WorkflowConstants.AUDIT_CAUSE_DISPLAY_NAME));
assertEquals("Wrong result in "+r, "Rejected", r.getResult());
}
displayCollection("notifications - process", dummyTransport.getMessages("dummy:simpleWorkflowNotifier-Processes"));
List<Message> notifications = dummyTransport.getMessages("dummy:simpleWorkflowNotifier-WorkItems");
displayCollection("notifications - work items", notifications);
for (Message notification : notifications) {
assertContains(notification, "Reason: Automatic rejection at deadline (timed action)");
assertContains(notification, "Result: REJECTED");
}
}
private void assertContains(Message notification, String text) {
if (!notification.getBody().contains(text)) {
fail("No '"+text+"' in "+notification);
}
}
}