/*
* Copyright (c) 2010-2013 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.general;
import com.evolveum.midpoint.model.api.context.ModelContext;
import com.evolveum.midpoint.model.api.context.ModelState;
import com.evolveum.midpoint.model.api.hooks.HookOperationMode;
import com.evolveum.midpoint.model.impl.AbstractInternalModelIntegrationTest;
import com.evolveum.midpoint.model.impl.controller.ModelOperationTaskHandler;
import com.evolveum.midpoint.model.impl.lens.Clockwork;
import com.evolveum.midpoint.model.impl.lens.LensContext;
import com.evolveum.midpoint.prism.PrismContext;
import com.evolveum.midpoint.prism.PrismObject;
import com.evolveum.midpoint.prism.path.ItemPath;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.task.api.Task;
import com.evolveum.midpoint.task.api.TaskExecutionStatus;
import com.evolveum.midpoint.task.api.TaskManager;
import com.evolveum.midpoint.test.AbstractIntegrationTest;
import com.evolveum.midpoint.test.Checker;
import com.evolveum.midpoint.test.IntegrationTestTools;
import com.evolveum.midpoint.test.util.TestUtil;
import com.evolveum.midpoint.util.exception.CommonException;
import com.evolveum.midpoint.util.exception.ObjectNotFoundException;
import com.evolveum.midpoint.util.exception.SchemaException;
import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;
import com.evolveum.midpoint.wf.impl.legacy.AbstractWfTestLegacy;
import com.evolveum.midpoint.wf.impl.activiti.ActivitiEngine;
import com.evolveum.midpoint.wf.impl.tasks.WfTaskUtil;
import com.evolveum.midpoint.wf.impl.processes.common.ActivitiUtil;
import com.evolveum.midpoint.wf.impl.processors.general.GeneralChangeProcessor;
import com.evolveum.midpoint.wf.impl.processors.primary.PrimaryChangeProcessor;
import com.evolveum.midpoint.wf.impl.util.MiscDataUtil;
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.annotation.DirtiesContext.ClassMode;
import org.springframework.test.context.ContextConfiguration;
import org.testng.annotations.Test;
import javax.xml.bind.JAXBException;
import java.io.File;
import java.util.List;
import static com.evolveum.midpoint.test.IntegrationTestTools.display;
import static org.testng.AssertJUnit.*;
/**
* @author semancik
*
*/
@ContextConfiguration(locations = {"classpath:ctx-workflow-test-main.xml"})
@DirtiesContext(classMode = ClassMode.AFTER_CLASS)
//@DependsOn("workflowServiceImpl")
public class TestGeneralChangeProcessor extends AbstractInternalModelIntegrationTest {
protected static final Trace LOGGER = TraceManager.getTrace(TestGeneralChangeProcessor.class);
private static final String TEST_RESOURCE_DIR_NAME = "src/test/resources";
private static final File REQ_USER_JACK_MODIFY_ADD_ASSIGNMENT_ROLE1 = new File(TEST_RESOURCE_DIR_NAME,
"legacy/user-jack-modify-add-assignment-role1.xml");
private static final String REQ_USER_JACK_MODIFY_ADD_ASSIGNMENT_ROLE2_CHANGE_GN = TEST_RESOURCE_DIR_NAME + "/legacy/user-jack-modify-add-assignment-role2-change-gn.xml";
private static final String REQ_USER_JACK_MODIFY_ADD_ASSIGNMENT_ROLE3_CHANGE_GN2 = TEST_RESOURCE_DIR_NAME + "/legacy/user-jack-modify-add-assignment-role3-change-gn2.xml";
private static final String REQ_USER_JACK_MODIFY_ADD_ASSIGNMENT_ROLES2_3_4 = TEST_RESOURCE_DIR_NAME + "/legacy/user-jack-modify-add-assignment-roles2-3-4.xml";
private static final String REQ_USER_JACK_MODIFY_ACTIVATION_DISABLE = TEST_RESOURCE_DIR_NAME + "/legacy/user-jack-modify-activation-disable.xml";
private static final String REQ_USER_JACK_MODIFY_ACTIVATION_ENABLE = TEST_RESOURCE_DIR_NAME + "/legacy/user-jack-modify-activation-enable.xml";
private static final String REQ_USER_JACK_MODIFY_CHANGE_PASSWORD = TEST_RESOURCE_DIR_NAME + "/legacy/user-jack-modify-change-password.xml";
private static final String REQ_USER_JACK_MODIFY_CHANGE_PASSWORD_2 = TEST_RESOURCE_DIR_NAME + "/legacy/user-jack-modify-change-password-2.xml";
private static final String DONT_CHECK = "dont-check";
@Autowired(required = true)
private Clockwork clockwork;
@Autowired(required = true)
private TaskManager taskManager;
@Autowired
private WfTaskUtil wfTaskUtil;
@Autowired
private ActivitiEngine activitiEngine;
@Autowired
private MiscDataUtil miscDataUtil;
@Autowired
private PrimaryChangeProcessor primaryChangeProcessor;
@Autowired
private GeneralChangeProcessor generalChangeProcessor;
@Autowired
private PrismContext prismContext;
private ActivitiUtil activitiUtil = new ActivitiUtil(); // this is not a spring bean
public TestGeneralChangeProcessor() throws JAXBException {
super();
}
@Override
public void initSystem(Task initTask, OperationResult initResult)
throws Exception {
super.initSystem(initTask, initResult);
importObjectFromFile(AbstractWfTestLegacy.USERS_AND_ROLES_FILE, initResult);
modifyObjectReplaceProperty(SystemConfigurationType.class, SystemObjectsType.SYSTEM_CONFIGURATION.value(),
new ItemPath(SystemConfigurationType.F_WORKFLOW_CONFIGURATION,
WfConfigurationType.F_PRIMARY_CHANGE_PROCESSOR,
PrimaryChangeProcessorConfigurationType.F_ENABLED),
initTask, initResult,
false);
modifyObjectReplaceProperty(SystemConfigurationType.class, SystemObjectsType.SYSTEM_CONFIGURATION.value(),
new ItemPath(SystemConfigurationType.F_WORKFLOW_CONFIGURATION,
WfConfigurationType.F_GENERAL_CHANGE_PROCESSOR,
GeneralChangeProcessorConfigurationType.F_ENABLED),
initTask, initResult,
true);
}
@Test
public void test010AddRole1() throws Exception {
TestUtil.displayTestTile(this, "test010UserModifyAddRole");
executeTest("test010UserModifyAddRole", USER_JACK_OID, 1, false, true, new ContextCreator() {
@Override
public LensContext createModelContext(OperationResult result) throws Exception {
LensContext<UserType> context = createUserLensContext();
fillContextWithUser(context, USER_JACK_OID, result);
addFocusModificationToContext(context, REQ_USER_JACK_MODIFY_ADD_ASSIGNMENT_ROLE1);
return context;
}
@Override
void assertsAfterClockworkRun(ModelContext context, Task task, OperationResult result) throws Exception {
assertEquals("Unexpected state of the context", ModelState.PRIMARY, context.getState());
}
@Override
void completeWorkItem(WorkItemType workItem, String taskId, OperationResult result) throws Exception {
// WorkItemContents contents = (WorkItemContents) workItem.getContents();
// PrismObject<? extends QuestionFormType> qFormObject = contents.getQuestionForm().asPrismObject();
// LOGGER.trace("workItemContents = " + qFormObject.debugDump());
//
// // change role1 -> role2
// final int N = 6;
// StringBuilder ctx = new StringBuilder();
//
// for (int ctxIndex = 0; ctxIndex < N; ctxIndex++) {
// QName contextQName = new QName(SchemaConstants.NS_WFCF, "modelContextToBeEdited" + ctxIndex);
// PrismProperty<String> contextProperty = qFormObject.findProperty(contextQName);
// assertNotNull(contextQName + " not found among workItem specific properties", contextProperty);
// ctx.append(contextProperty.getRealValue());
// }
//
// String newCtx = ctx.toString().replaceAll("00000001-d34d-b33f-f00d-000000000001", "00000001-d34d-b33f-f00d-000000000002");
// for (int ctxIndex = 0; ctxIndex < N; ctxIndex++) {
// QName contextQName = new QName(SchemaConstants.NS_WFCF, "modelContextToBeEdited" + ctxIndex);
// PrismProperty<String> contextProperty = qFormObject.findProperty(contextQName);
// contextProperty.replace(new PrismPropertyValue<>(JaxbValueContainer.getChunk(newCtx, ctxIndex)));
// }
//
// login(getUser(USER_ADMINISTRATOR_OID));
//// workflowServiceImpl.completeWorkItem(taskId, qFormObject, "approve", result);
}
@Override
void assertsRootTaskFinishes(Task task, OperationResult result) throws Exception {
assertAssignedRole(USER_JACK_OID, AbstractWfTestLegacy.ROLE_R2_OID, task, result);
checkDummyTransportMessages("simpleUserNotifier", 1);
//checkWorkItemAuditRecords(createResultMap(AbstractWfTestLegacy.ROLE_R1_OID, WorkflowResult.APPROVED));
//checkUserApprovers(USER_JACK_OID, Arrays.asList(AbstractWfTestLegacy.R1BOSS_OID), result);
}
});
}
@Test
public void test020AddAccountRejected() throws Exception {
TestUtil.displayTestTile(this, "test020AddAccountRejected");
enableDisableScenarios(false, true);
executeTest("test020AddAccountRejected", USER_JACK_OID, 1, false, true, new ContextCreator() {
@Override
public LensContext createModelContext(OperationResult result) throws Exception {
LensContext<UserType> context = createUserLensContext();
fillContextWithUser(context, USER_JACK_OID, result);
addModificationToContextAddAccountFromFile(context, ACCOUNT_SHADOW_JACK_DUMMY_FILE);
return context;
}
@Override
void assertsAfterClockworkRun(ModelContext context, Task task, OperationResult result) throws Exception {
assertEquals("Unexpected state of the context", ModelState.SECONDARY, context.getState());
}
@Override
void completeWorkItem(WorkItemType workItem, String taskId, OperationResult result) throws Exception {
//
// PrismObject<? extends WorkItemContents> workItemContents = workItem.getContents().asPrismObject();
// display("workItemContents", workItemContents);
//
// PrismObject<? extends QuestionFormType> questionFormPrism = workItemContents.asObjectable().getQuestionForm().asPrismObject();
//
// WfProcessInstanceType instance = null; //workflowServiceImpl.getProcessInstanceById(workItem.getProcessInstanceId(), false, true, result);
// PrismProperty<ObjectDeltaType> dummyResourceDelta = null; // TODO ((ProcessInstanceState) instance.getState()).getProcessSpecificState().asPrismContainerValue().findProperty(ApprovingDummyResourceChangesScenarioBean.DUMMY_RESOURCE_DELTA_QNAME);
// ObjectDeltaType deltaType = dummyResourceDelta.getRealValue();
// display("dummyResourceDelta", DeltaConvertor.createObjectDelta(deltaType, prismContext));
//
// PrismPropertyDefinition ppd = new PrismPropertyDefinitionImpl(new QName(SchemaConstants.NS_WFCF, "[Button]rejectAll"),
// DOMUtil.XSD_BOOLEAN, prismContext);
// PrismProperty<Boolean> rejectAll = ppd.instantiate();
// rejectAll.setRealValue(Boolean.TRUE);
// questionFormPrism.addReplaceExisting(rejectAll);
//
// login(getUser(USER_ADMINISTRATOR_OID));
//// workflowServiceImpl.completeWorkItem(taskId, questionFormPrism, "rejectAll", result);
}
@Override
void assertsRootTaskFinishes(Task task, OperationResult result) throws Exception {
PrismObject<UserType> jack = getUser(USER_JACK_OID);
// assertAssignedRole(USER_JACK_OID, AbstractWfTestLegacy.ROLE_R2_OID, task, result);
assertNoLinkedAccount(jack);
//checkDummyTransportMessages("simpleUserNotifier", 1);
//checkWorkItemAuditRecords(createResultMap(AbstractWfTestLegacy.ROLE_R1_OID, WorkflowResult.APPROVED));
//checkUserApprovers(USER_JACK_OID, Arrays.asList(AbstractWfTestLegacy.R1BOSS_OID), result);
}
});
}
protected void enableDisableScenarios(boolean... values) throws ObjectNotFoundException, SchemaException, com.evolveum.midpoint.util.exception.ExpressionEvaluationException, com.evolveum.midpoint.util.exception.CommunicationException, com.evolveum.midpoint.util.exception.ConfigurationException, com.evolveum.midpoint.util.exception.ObjectAlreadyExistsException, com.evolveum.midpoint.util.exception.PolicyViolationException, com.evolveum.midpoint.util.exception.SecurityViolationException {
OperationResult result = new OperationResult("execution");
Task task = taskManager.createTaskInstance("execution");
GeneralChangeProcessorConfigurationType gcpConfig = getSystemConfiguration().getWorkflowConfiguration().getGeneralChangeProcessor();
for (int i = 0; i < values.length; i++) {
gcpConfig.getScenario().get(i).setEnabled(values[i]);
}
modifyObjectReplaceProperty(SystemConfigurationType.class, SystemObjectsType.SYSTEM_CONFIGURATION.value(),
new ItemPath(SystemConfigurationType.F_WORKFLOW_CONFIGURATION,
WfConfigurationType.F_GENERAL_CHANGE_PROCESSOR,
GeneralChangeProcessorConfigurationType.F_ENABLED),
task, result,
gcpConfig);
}
// @Test(enabled = false)
// public void test028CurrentRepo() throws Exception {
// TestUtil.displayTestTile(this, "test029NewRepo");
//
// //old repo
// PrismDomProcessor domProcessor = prismContext.getPrismDomProcessor();
// //"extension" value
// String xml = IOUtils.toString(new FileInputStream("./src/test/resources/model-context.xml"), "utf-8");
// Element root = DOMUtil.parseDocument(xml).getDocumentElement();
//
// QName name = new QName("http://midpoint.evolveum.com/xml/ns/public/model/model-context-3", "modelContext");
//
// PrismObjectDefinition oDef = prismContext.getSchemaRegistry().findObjectDefinitionByCompileTimeClass(TaskType.class);
// PrismContainerDefinition def = oDef.findContainerDefinition(new ItemPath(ObjectType.F_EXTENSION, name));
// Item parsedItem = domProcessor.parseItem(DOMUtil.listChildElements(root), name, def);
// LOGGER.debug("Parser:\n{}", parsedItem.debugDump());
// }
// @Test(enabled = false)
// public void test029NewRepo() throws Exception {
// TestUtil.displayTestTile(this, "test029NewRepo");
//
// PrismDomProcessor domProcessor = prismContext.getPrismDomProcessor();
// String xml = IOUtils.toString(new FileInputStream("./src/test/resources/task.xml"), "utf-8");
// PrismObject o = domProcessor.parseObject(xml);
// LOGGER.info("Parsed:\n{}", o.debugDump());
// }
@Test
public void test030AddAccountApproved() throws Exception {
TestUtil.displayTestTile(this, "test030AddAccountApproved");
enableDisableScenarios(false, true);
executeTest("test030AddAccountApproved", USER_JACK_OID, 1, false, true, new ContextCreator() {
@Override
public LensContext createModelContext(OperationResult result) throws Exception {
LensContext<UserType> context = createUserLensContext();
fillContextWithUser(context, USER_JACK_OID, result);
addModificationToContextAddAccountFromFile(context, ACCOUNT_SHADOW_JACK_DUMMY_FILE);
return context;
}
@Override
void assertsAfterClockworkRun(ModelContext context, Task task, OperationResult result) throws Exception {
assertEquals("Unexpected state of the context", ModelState.SECONDARY, context.getState());
}
@Override
void completeWorkItem(WorkItemType workItem, String taskId, OperationResult result) throws Exception {
//
// PrismObject<? extends WorkItemContents> workItemContents = workItem.getContents().asPrismObject();
// display("workItemContents", workItemContents);
//
// PrismObject<? extends QuestionFormType> questionFormPrism = workItemContents.asObjectable().getQuestionForm().asPrismObject();
//
// WfProcessInstanceType instance = null; //workflowServiceImpl.getProcessInstanceById(workItem.getProcessInstanceId(), false, true, result);
// PrismProperty<ObjectDeltaType> dummyResourceDelta = null; // TODO ((ProcessInstanceState) instance.getState()).getProcessSpecificState().asPrismContainerValue().findProperty(ApprovingDummyResourceChangesScenarioBean.DUMMY_RESOURCE_DELTA_QNAME);
// ObjectDeltaType deltaType = dummyResourceDelta.getRealValue();
// display("dummyResourceDelta", DeltaConvertor.createObjectDelta(deltaType, prismContext));
//
// PrismPropertyDefinition ppd = new PrismPropertyDefinitionImpl(new QName(SchemaConstants.NS_WFCF, "[Button]approve"),
// DOMUtil.XSD_BOOLEAN, prismContext);
// PrismProperty<Boolean> approve = ppd.instantiate();
// approve.setRealValue(Boolean.TRUE);
// questionFormPrism.addReplaceExisting(approve);
//
// login(getUser(USER_ADMINISTRATOR_OID));
//// workflowServiceImpl.completeWorkItem(taskId, questionFormPrism, "approve", result);
}
@Override
void assertsRootTaskFinishes(Task task, OperationResult result) throws Exception {
PrismObject<UserType> jack = getUser(USER_JACK_OID);
// assertAssignedRole(USER_JACK_OID, AbstractWfTestLegacy.ROLE_R2_OID, task, result);
assertAccount(jack, RESOURCE_DUMMY_OID);
//checkDummyTransportMessages("simpleUserNotifier", 1);
//checkWorkItemAuditRecords(createResultMap(AbstractWfTestLegacy.ROLE_R1_OID, WorkflowResult.APPROVED));
//checkUserApprovers(USER_JACK_OID, Arrays.asList(AbstractWfTestLegacy.R1BOSS_OID), result);
}
});
}
@Test
public void test000LoadContext() throws Exception {
TestUtil.displayTestTile(this, "test000LoadContext");
OperationResult result = new OperationResult("test000LoadContext");
LensContextType lensContextType = prismContext.parserFor(new File("src/test/resources/model-contexts/context-dummy-resource.xml")).xml().parseRealValue(LensContextType.class);
display("LensContextType", lensContextType);
LensContext<?> lensContext = LensContext.fromLensContextType(lensContextType, prismContext, provisioningService, result);
display("LensContext", lensContext);
}
private abstract class ContextCreator {
LensContext createModelContext(OperationResult result) throws Exception { return null; }
void assertsAfterClockworkRun(ModelContext context, Task task, OperationResult result) throws Exception { }
void assertsAfterImmediateExecutionFinished(Task task, OperationResult result) throws Exception { }
void assertsRootTaskFinishes(Task task, OperationResult result) throws Exception { }
String getObjectOid(Task task, OperationResult result) throws SchemaException { return null; };
abstract void completeWorkItem(WorkItemType workItem, String taskId, OperationResult result) throws Exception;
}
private void executeTest(String testName, String oid, int subtaskCount, boolean immediate, boolean checkObjectOnSubtasks, ContextCreator contextCreator) throws Exception {
int workflowSubtaskCount = immediate ? subtaskCount-1 : subtaskCount;
// GIVEN
prepareNotifications();
dummyAuditService.clear();
OperationResult result = new OperationResult("execution");
Task modelTask = taskManager.createTaskInstance(TestGeneralChangeProcessor.class.getName() + "."+testName);
display("Model task after creation", modelTask);
LensContext<UserType> context = (LensContext<UserType>) contextCreator.createModelContext(result);
modelTask.setOwner(repositoryService.getObject(UserType.class, USER_ADMINISTRATOR_OID, null, result));
display("Input context", context);
assertFocusModificationSanity(context);
// WHEN
HookOperationMode mode = clockwork.run(context, modelTask, result);
// THEN
contextCreator.assertsAfterClockworkRun(context, modelTask, result);
assertEquals("Wrong mode after clockwork.run in " + context.getState(), HookOperationMode.BACKGROUND, mode);
modelTask.refresh(result);
display("Model task after clockwork runs", modelTask);
Task rootTask = taskManager.getTask(wfTaskUtil.getRootTaskOid(modelTask), result);
display("Workflow root task created by clockwork run", rootTask);
assertTrue("Workflow root task is not persistent", rootTask.isPersistent());
assertEquals("Invalid current handler", ModelOperationTaskHandler.MODEL_OPERATION_TASK_URI, rootTask.getHandlerUri());
ModelContext taskModelContext = immediate ? null : wfTaskUtil.getModelContext(rootTask, result);
assertNotNull("Model context is not present in root task", taskModelContext);
List<Task> subtasks = rootTask.listSubtasks(result);
assertEquals("Incorrect number of subtasks", subtaskCount, subtasks.size());
for (int subtaskIndex = 0; subtaskIndex < subtasks.size(); subtaskIndex++) {
Task subtask = subtasks.get(subtaskIndex);
// now check the workflow state
String pid = wfTaskUtil.getProcessId(subtask);
assertNotNull("Workflow process instance id not present in subtask " + subtask, pid);
/*
WfProcessInstanceType processInstance = null; //workflowServiceImpl.getProcessInstanceById(pid, false, true, result);
assertNotNull("Process instance information cannot be retrieved", processInstance);
assertEquals("Incorrect number of work items", 1, processInstance.getWorkItems().size());
String taskId = processInstance.getWorkItems().get(0).getWorkItemId();
//WorkItemNewType workItem = workflowServiceImpl.getWorkItemDetailsById(taskId, result);
WorkItemNewType workItem = null; // TODO
org.activiti.engine.task.Task t = activitiEngine.getTaskService().createTaskQuery().taskId(taskId).singleResult();
assertNotNull("activiti task not found", t);
String executionId = t.getExecutionId();
LOGGER.trace("Task id = " + taskId + ", execution id = " + executionId);
contextCreator.completeWorkItem(workItem, taskId, result);
*/
}
waitForTaskClose(rootTask, 60000);
contextCreator.assertsRootTaskFinishes(rootTask, result);
}
protected void waitForTaskClose(final Task task, final int timeout) throws Exception {
final OperationResult waitResult = new OperationResult(AbstractIntegrationTest.class+".waitForTaskClose");
Checker checker = new Checker() {
@Override
public boolean check() throws CommonException {
task.refresh(waitResult);
OperationResult result = task.getResult();
if (verbose) display("Check result", result);
return task.getExecutionStatus() == TaskExecutionStatus.CLOSED;
}
@Override
public void timeout() {
try {
task.refresh(waitResult);
} catch (ObjectNotFoundException e) {
LOGGER.error("Exception during task refresh: {}", e,e);
} catch (SchemaException e) {
LOGGER.error("Exception during task refresh: {}", e,e);
}
OperationResult result = task.getResult();
LOGGER.debug("Result of timed-out task:\n{}", result.debugDump());
assert false : "Timeout ("+timeout+") while waiting for "+task+" to finish. Last result "+result;
}
};
IntegrationTestTools.waitFor("Waiting for " + task + " finish", checker, timeout, 1000);
}
}