com.evolveum.midpoint.wf.impl.policy.AbstractWfTestPolicy.java Source code

Java tutorial

Introduction

Here is the source code for com.evolveum.midpoint.wf.impl.policy.AbstractWfTestPolicy.java

Source

/*
 * 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;

import com.evolveum.midpoint.model.api.ModelExecuteOptions;
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.AbstractModelImplementationIntegrationTest;
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.*;
import com.evolveum.midpoint.prism.delta.ObjectDelta;
import com.evolveum.midpoint.prism.path.ItemPath;
import com.evolveum.midpoint.prism.query.builder.QueryBuilder;
import com.evolveum.midpoint.prism.query.builder.S_AtomicFilterExit;
import com.evolveum.midpoint.prism.util.PrismUtil;
import com.evolveum.midpoint.schema.GetOperationOptions;
import com.evolveum.midpoint.schema.SearchResultList;
import com.evolveum.midpoint.schema.SelectorOptions;
import com.evolveum.midpoint.schema.constants.ObjectTypes;
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.security.api.SecurityUtil;
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.util.exception.*;
import com.evolveum.midpoint.wf.api.WorkflowManager;
import com.evolveum.midpoint.wf.impl.WfTestUtil;
import com.evolveum.midpoint.wf.impl.activiti.ActivitiEngine;
import com.evolveum.midpoint.wf.impl.processes.common.CommonProcessVariableNames;
import com.evolveum.midpoint.wf.impl.processes.common.LightweightObjectRef;
import com.evolveum.midpoint.wf.impl.WorkflowResult;
import com.evolveum.midpoint.wf.impl.processors.general.GeneralChangeProcessor;
import com.evolveum.midpoint.wf.impl.processors.primary.PrimaryChangeProcessor;
import com.evolveum.midpoint.wf.impl.tasks.WfTaskUtil;
import com.evolveum.midpoint.wf.impl.util.MiscDataUtil;
import com.evolveum.midpoint.wf.util.QueryUtils;
import com.evolveum.midpoint.xml.ns._public.common.common_3.*;
import org.apache.commons.collections4.CollectionUtils;
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 javax.xml.namespace.QName;
import java.io.File;
import java.io.IOException;
import java.util.*;

import static com.evolveum.midpoint.prism.PrismConstants.T_PARENT;
import static com.evolveum.midpoint.schema.GetOperationOptions.createRetrieve;
import static com.evolveum.midpoint.schema.GetOperationOptions.resolveItemsNamed;
import static com.evolveum.midpoint.xml.ns._public.common.common_3.TaskType.F_WORKFLOW_CONTEXT;
import static com.evolveum.midpoint.xml.ns._public.common.common_3.WfContextType.*;
import static com.evolveum.midpoint.xml.ns._public.common.common_3.WfPrimaryChangeProcessorStateType.F_DELTAS_TO_PROCESS;
import static com.evolveum.midpoint.xml.ns._public.common.common_3.WorkItemType.*;
import static java.util.Collections.singleton;
import static java.util.Collections.singletonList;
import static org.testng.AssertJUnit.*;

/**
 * @author mederly
 *
 */
@ContextConfiguration(locations = { "classpath:ctx-workflow-test-main.xml" })
@DirtiesContext(classMode = ClassMode.AFTER_CLASS)
public class AbstractWfTestPolicy extends AbstractModelImplementationIntegrationTest {

    protected static final File TEST_RESOURCE_DIR = new File("src/test/resources/policy");
    private static final File SYSTEM_CONFIGURATION_FILE = new File(TEST_RESOURCE_DIR, "system-configuration.xml");
    public static final File ROLE_SUPERUSER_FILE = new File(TEST_RESOURCE_DIR, "role-superuser.xml");
    public static final File USER_ADMINISTRATOR_FILE = new File(TEST_RESOURCE_DIR, "user-administrator.xml");

    protected static final File USER_JACK_FILE = new File(TEST_RESOURCE_DIR, "user-jack.xml");
    protected static final File USER_JACK_DEPUTY_FILE = new File(TEST_RESOURCE_DIR, "user-jack-deputy.xml"); // delegation is created only when needed
    protected static final File USER_BOB_FILE = new File(TEST_RESOURCE_DIR, "user-bob.xml");
    protected static final File USER_CHUCK_FILE = new File(TEST_RESOURCE_DIR, "user-chuck.xml");
    protected static final File USER_LEAD1_FILE = new File(TEST_RESOURCE_DIR, "user-lead1.xml");
    protected static final File USER_LEAD1_DEPUTY_1_FILE = new File(TEST_RESOURCE_DIR, "user-lead1-deputy1.xml");
    protected static final File USER_LEAD1_DEPUTY_2_FILE = new File(TEST_RESOURCE_DIR, "user-lead1-deputy2.xml");
    protected static final File USER_LEAD2_FILE = new File(TEST_RESOURCE_DIR, "user-lead2.xml");
    protected static final File USER_LEAD3_FILE = new File(TEST_RESOURCE_DIR, "user-lead3.xml");
    protected static final File USER_LEAD10_FILE = new File(TEST_RESOURCE_DIR, "user-lead10.xml");
    protected static final File USER_LEAD15_FILE = new File(TEST_RESOURCE_DIR, "user-lead15.xml");
    protected static final File USER_SECURITY_APPROVER_FILE = new File(TEST_RESOURCE_DIR,
            "user-security-approver.xml");
    protected static final File USER_SECURITY_APPROVER_DEPUTY_FILE = new File(TEST_RESOURCE_DIR,
            "user-security-approver-deputy.xml");
    protected static final File USER_SECURITY_APPROVER_DEPUTY_LIMITED_FILE = new File(TEST_RESOURCE_DIR,
            "user-security-approver-deputy-limited.xml");

    protected static final File ROLE_APPROVER_FILE = new File(TEST_RESOURCE_DIR, "041-role-approver.xml");
    protected static final File METAROLE_DEFAULT_FILE = new File(TEST_RESOURCE_DIR, "metarole-default.xml");
    protected static final File METAROLE_SECURITY_FILE = new File(TEST_RESOURCE_DIR, "metarole-security.xml");
    // following 2 are not used by default (assigned when necessary)
    protected static final File METAROLE_PRUNE_TEST2X_ROLES_FILE = new File(TEST_RESOURCE_DIR,
            "metarole-prune-test2x-roles.xml");
    protected static final File METAROLE_APPROVE_UNASSIGN_FILE = new File(TEST_RESOURCE_DIR,
            "metarole-approve-unassign.xml");
    protected static final File ROLE_ROLE1_FILE = new File(TEST_RESOURCE_DIR, "role-role1.xml");
    protected static final File ROLE_ROLE1A_FILE = new File(TEST_RESOURCE_DIR, "role-role1a.xml");
    protected static final File ROLE_ROLE1B_FILE = new File(TEST_RESOURCE_DIR, "role-role1b.xml");
    protected static final File ROLE_ROLE2_FILE = new File(TEST_RESOURCE_DIR, "role-role2.xml");
    protected static final File ROLE_ROLE2A_FILE = new File(TEST_RESOURCE_DIR, "role-role2a.xml");
    protected static final File ROLE_ROLE2B_FILE = new File(TEST_RESOURCE_DIR, "role-role2b.xml");
    protected static final File ROLE_ROLE3_FILE = new File(TEST_RESOURCE_DIR, "role-role3.xml");
    protected static final File ROLE_ROLE3A_FILE = new File(TEST_RESOURCE_DIR, "role-role3a.xml");
    protected static final File ROLE_ROLE3B_FILE = new File(TEST_RESOURCE_DIR, "role-role3b.xml");
    protected static final File ROLE_ROLE4_FILE = new File(TEST_RESOURCE_DIR, "role-role4.xml");
    protected static final File ROLE_ROLE4A_FILE = new File(TEST_RESOURCE_DIR, "role-role4a.xml");
    protected static final File ROLE_ROLE4B_FILE = new File(TEST_RESOURCE_DIR, "role-role4b.xml");
    protected static final File ROLE_ROLE10_FILE = new File(TEST_RESOURCE_DIR, "role-role10.xml");
    protected static final File ROLE_ROLE10A_FILE = new File(TEST_RESOURCE_DIR, "role-role10a.xml");
    protected static final File ROLE_ROLE10B_FILE = new File(TEST_RESOURCE_DIR, "role-role10b.xml");
    protected static final File ROLE_ROLE15_FILE = new File(TEST_RESOURCE_DIR, "role-role15.xml");
    protected static final File ROLE_FOCUS_ASSIGNMENT_MAPPING = new File(TEST_RESOURCE_DIR,
            "role-focus-assignment-mapping.xml");

    protected static final File USER_TEMPLATE_ASSIGNING_ROLE_1A = new File(TEST_RESOURCE_DIR,
            "user-template-assigning-role1a.xml");
    protected static final File USER_TEMPLATE_ASSIGNING_ROLE_1A_AFTER = new File(TEST_RESOURCE_DIR,
            "user-template-assigning-role1a-after.xml");

    protected static final String USER_ADMINISTRATOR_OID = SystemObjectsType.USER_ADMINISTRATOR.value();

    protected String userJackOid;
    protected String userJackDeputyOid;
    protected String userBobOid;
    protected String userChuckOid;
    protected String userLead1Oid;
    protected String userLead1Deputy1Oid;
    protected String userLead1Deputy2Oid;
    protected String userLead2Oid;
    protected String userLead3Oid;
    protected String userLead10Oid; // imported later
    protected String userLead15Oid;
    protected String userSecurityApproverOid;
    protected String userSecurityApproverDeputyOid;
    protected String userSecurityApproverDeputyLimitedOid;

    protected String roleApproverOid;
    protected String metaroleDefaultOid;
    protected String metaroleSecurityOid;
    protected String metarolePruneTest2xRolesOid;
    protected String metaroleApproveUnassign;
    protected String roleRole1Oid;
    protected String roleRole1aOid;
    protected String roleRole1bOid;
    protected String roleRole2Oid;
    protected String roleRole2aOid;
    protected String roleRole2bOid;
    protected String roleRole3Oid;
    protected String roleRole3aOid;
    protected String roleRole3bOid;
    protected String roleRole4Oid;
    protected String roleRole4aOid;
    protected String roleRole4bOid;
    protected String roleRole10Oid;
    protected String roleRole10aOid;
    protected String roleRole10bOid;
    protected String roleRole15Oid;
    protected String roleFocusAssignmentMapping;

    protected String userTemplateAssigningRole1aOid;
    protected String userTemplateAssigningRole1aOidAfter;

    @Autowired
    protected Clockwork clockwork;
    @Autowired
    protected TaskManager taskManager;
    @Autowired
    protected WorkflowManager workflowManager;
    @Autowired
    protected WfTaskUtil wfTaskUtil;
    @Autowired
    protected ActivitiEngine activitiEngine;
    @Autowired
    protected MiscDataUtil miscDataUtil;
    @Autowired
    protected PrimaryChangeProcessor primaryChangeProcessor;
    @Autowired
    protected GeneralChangeProcessor generalChangeProcessor;

    protected PrismObject<UserType> userAdministrator;

    @Override
    public void initSystem(Task initTask, OperationResult initResult) throws Exception {
        super.initSystem(initTask, initResult);
        modelService.postInit(initResult);

        PrismObject<SystemConfigurationType> sysconfig = prismContext.parseObject(getSystemConfigurationFile());
        updateSystemConfiguration(sysconfig.asObjectable());
        repoAddObject(sysconfig, initResult);

        repoAddObjectFromFile(ROLE_SUPERUSER_FILE, initResult);
        userAdministrator = repoAddObjectFromFile(USER_ADMINISTRATOR_FILE, initResult);
        login(userAdministrator);

        roleApproverOid = repoAddObjectFromFile(ROLE_APPROVER_FILE, initResult).getOid();
        metaroleDefaultOid = repoAddObjectFromFile(METAROLE_DEFAULT_FILE, initResult).getOid();
        metaroleSecurityOid = repoAddObjectFromFile(METAROLE_SECURITY_FILE, initResult).getOid();
        metarolePruneTest2xRolesOid = repoAddObjectFromFile(METAROLE_PRUNE_TEST2X_ROLES_FILE, initResult).getOid();
        metaroleApproveUnassign = repoAddObjectFromFile(METAROLE_APPROVE_UNASSIGN_FILE, initResult).getOid();

        userJackOid = repoAddObjectFromFile(USER_JACK_FILE, initResult).getOid();
        userJackDeputyOid = repoAddObjectFromFile(USER_JACK_DEPUTY_FILE, initResult).getOid();
        userBobOid = repoAddObjectFromFile(USER_BOB_FILE, initResult).getOid();
        userChuckOid = repoAddObjectFromFile(USER_CHUCK_FILE, initResult).getOid();
        roleRole1Oid = repoAddObjectFromFile(ROLE_ROLE1_FILE, initResult).getOid();
        roleRole1aOid = repoAddObjectFromFile(ROLE_ROLE1A_FILE, initResult).getOid();
        roleRole1bOid = repoAddObjectFromFile(ROLE_ROLE1B_FILE, initResult).getOid();
        roleRole2Oid = repoAddObjectFromFile(ROLE_ROLE2_FILE, initResult).getOid();
        roleRole2aOid = repoAddObjectFromFile(ROLE_ROLE2A_FILE, initResult).getOid();
        roleRole2bOid = repoAddObjectFromFile(ROLE_ROLE2B_FILE, initResult).getOid();
        roleRole3Oid = repoAddObjectFromFile(ROLE_ROLE3_FILE, initResult).getOid();
        roleRole3aOid = repoAddObjectFromFile(ROLE_ROLE3A_FILE, initResult).getOid();
        roleRole3bOid = repoAddObjectFromFile(ROLE_ROLE3B_FILE, initResult).getOid();
        roleRole4Oid = repoAddObjectFromFile(ROLE_ROLE4_FILE, initResult).getOid();
        roleRole4aOid = repoAddObjectFromFile(ROLE_ROLE4A_FILE, initResult).getOid();
        roleRole4bOid = repoAddObjectFromFile(ROLE_ROLE4B_FILE, initResult).getOid();
        roleRole10Oid = repoAddObjectFromFile(ROLE_ROLE10_FILE, initResult).getOid();
        roleRole10aOid = repoAddObjectFromFile(ROLE_ROLE10A_FILE, initResult).getOid();
        roleRole10bOid = repoAddObjectFromFile(ROLE_ROLE10B_FILE, initResult).getOid();
        roleRole15Oid = repoAddObjectFromFile(ROLE_ROLE15_FILE, initResult).getOid();
        roleFocusAssignmentMapping = repoAddObjectFromFile(ROLE_FOCUS_ASSIGNMENT_MAPPING, initResult).getOid();

        userLead1Oid = addAndRecomputeUser(USER_LEAD1_FILE, initTask, initResult);
        userLead2Oid = addAndRecomputeUser(USER_LEAD2_FILE, initTask, initResult);
        userLead3Oid = addAndRecomputeUser(USER_LEAD3_FILE, initTask, initResult);
        userLead15Oid = addAndRecomputeUser(USER_LEAD15_FILE, initTask, initResult);
        // LEAD10 will be imported later!
        userSecurityApproverOid = addAndRecomputeUser(USER_SECURITY_APPROVER_FILE, initTask, initResult);
        userSecurityApproverDeputyOid = addAndRecomputeUser(USER_SECURITY_APPROVER_DEPUTY_FILE, initTask,
                initResult);
        userSecurityApproverDeputyLimitedOid = addAndRecomputeUser(USER_SECURITY_APPROVER_DEPUTY_LIMITED_FILE,
                initTask, initResult);

        userTemplateAssigningRole1aOid = repoAddObjectFromFile(USER_TEMPLATE_ASSIGNING_ROLE_1A, initResult)
                .getOid();
        userTemplateAssigningRole1aOidAfter = repoAddObjectFromFile(USER_TEMPLATE_ASSIGNING_ROLE_1A_AFTER,
                initResult).getOid();
    }

    @Override
    protected PrismObject<UserType> getDefaultActor() {
        return userAdministrator;
    }

    protected void updateSystemConfiguration(SystemConfigurationType systemConfiguration)
            throws SchemaException, IOException {
        // nothing to do by default
    }

    protected File getSystemConfigurationFile() {
        return SYSTEM_CONFIGURATION_FILE;
    }

    protected void importLead10(Task task, OperationResult result) throws Exception {
        userLead10Oid = addAndRecomputeUser(USER_LEAD10_FILE, task, result);
    }

    protected void importLead1Deputies(Task task, OperationResult result) throws Exception {
        userLead1Deputy1Oid = addAndRecomputeUser(USER_LEAD1_DEPUTY_1_FILE, task, result);
        userLead1Deputy2Oid = addAndRecomputeUser(USER_LEAD1_DEPUTY_2_FILE, task, result);
    }

    protected Map<String, WorkflowResult> createResultMap(String oid, WorkflowResult result) {
        Map<String, WorkflowResult> retval = new HashMap<>();
        retval.put(oid, result);
        return retval;
    }

    protected Map<String, WorkflowResult> createResultMap(String oid, WorkflowResult approved, String oid2,
            WorkflowResult approved2) {
        Map<String, WorkflowResult> retval = new HashMap<>();
        retval.put(oid, approved);
        retval.put(oid2, approved2);
        return retval;
    }

    protected Map<String, WorkflowResult> createResultMap(String oid, WorkflowResult approved, String oid2,
            WorkflowResult approved2, String oid3, WorkflowResult approved3) {
        Map<String, WorkflowResult> retval = new HashMap<>();
        retval.put(oid, approved);
        retval.put(oid2, approved2);
        retval.put(oid3, approved3);
        return retval;
    }

    protected void checkAuditRecords(Map<String, WorkflowResult> expectedResults) {
        checkWorkItemAuditRecords(expectedResults);
        checkWfProcessAuditRecords(expectedResults);
    }

    protected void checkWorkItemAuditRecords(Map<String, WorkflowResult> expectedResults) {
        WfTestUtil.checkWorkItemAuditRecords(expectedResults, dummyAuditService);
    }

    protected void checkWfProcessAuditRecords(Map<String, WorkflowResult> expectedResults) {
        WfTestUtil.checkWfProcessAuditRecords(expectedResults, dummyAuditService);
    }

    protected void removeAllAssignments(String oid, OperationResult result) throws Exception {
        PrismObject<UserType> user = repositoryService.getObject(UserType.class, oid, null, result);
        for (AssignmentType at : user.asObjectable().getAssignment()) {
            ObjectDelta delta = ObjectDelta.createModificationDeleteContainer(UserType.class, oid,
                    UserType.F_ASSIGNMENT, prismContext, at.asPrismContainerValue().clone());
            repositoryService.modifyObject(UserType.class, oid, delta.getModifications(), result);
            display("Removed assignment " + at + " from " + user);
        }
    }

    public void createObject(final String TEST_NAME, ObjectType object, boolean immediate, boolean approve,
            String assigneeOid) throws Exception {
        ObjectDelta<RoleType> addObjectDelta = ObjectDelta.createAddDelta((PrismObject) object.asPrismObject());

        executeTest(TEST_NAME, new TestDetails() {
            @Override
            protected LensContext createModelContext(OperationResult result) throws Exception {
                LensContext<RoleType> lensContext = createLensContext((Class) object.getClass());
                addFocusDeltaToContext(lensContext, addObjectDelta);
                return lensContext;
            }

            @Override
            protected void afterFirstClockworkRun(Task rootTask, List<Task> subtasks, List<WorkItemType> workItems,
                    OperationResult result) throws Exception {
                if (immediate) {
                    assertFalse("There is model context in the root task (it should not be there)",
                            wfTaskUtil.hasModelContext(rootTask));
                } else {
                    ModelContext taskModelContext = wfTaskUtil.getModelContext(rootTask, result);
                    ObjectDelta realDelta0 = taskModelContext.getFocusContext().getPrimaryDelta();
                    assertTrue("Non-empty primary focus delta: " + realDelta0.debugDump(), realDelta0.isEmpty());
                    assertNoObject(object);
                    ExpectedTask expectedTask = new ExpectedTask(null,
                            "Adding role \"" + object.getName().getOrig() + "\"");
                    ExpectedWorkItem expectedWorkItem = new ExpectedWorkItem(assigneeOid, null, expectedTask);
                    assertWfContextAfterClockworkRun(rootTask, subtasks, workItems, result, null,
                            Collections.singletonList(expectedTask), Collections.singletonList(expectedWorkItem));
                }
            }

            @Override
            protected void afterTask0Finishes(Task task, OperationResult result) throws Exception {
                assertNoObject(object);
            }

            @Override
            protected void afterRootTaskFinishes(Task task, List<Task> subtasks, OperationResult result)
                    throws Exception {
                if (approve) {
                    assertObject(object);
                } else {
                    assertNoObject(object);
                }
            }

            @Override
            protected boolean executeImmediately() {
                return immediate;
            }

            @Override
            public List<ApprovalInstruction> getApprovalSequence() {
                return singletonList(new ApprovalInstruction(null, true, userLead1Oid, "creation comment"));
            }
        }, 1);
    }

    public <T extends ObjectType> void modifyObject(final String TEST_NAME, ObjectDelta<T> objectDelta,
            ObjectDelta<T> expectedDelta0, ObjectDelta<T> expectedDelta1, boolean immediate, boolean approve,
            String assigneeOid, List<ExpectedTask> expectedTasks, List<ExpectedWorkItem> expectedWorkItems,
            Runnable assertDelta0Executed, Runnable assertDelta1NotExecuted, Runnable assertDelta1Executed)
            throws Exception {

        executeTest(TEST_NAME, new TestDetails() {
            @Override
            protected LensContext createModelContext(OperationResult result) throws Exception {
                Class<T> clazz = objectDelta.getObjectTypeClass();
                //PrismObject<T> object = getObject(clazz, objectDelta.getOid());
                LensContext<T> lensContext = createLensContext(clazz);
                addFocusDeltaToContext(lensContext, objectDelta);
                return lensContext;
            }

            @Override
            protected void afterFirstClockworkRun(Task rootTask, List<Task> subtasks, List<WorkItemType> workItems,
                    OperationResult result) throws Exception {
                if (immediate) {
                    assertFalse("There is model context in the root task (it should not be there)",
                            wfTaskUtil.hasModelContext(rootTask));
                } else {
                    ModelContext taskModelContext = wfTaskUtil.getModelContext(rootTask, result);
                    ObjectDelta realDelta0 = taskModelContext.getFocusContext().getPrimaryDelta();
                    assertDeltasEqual("Wrong delta left as primary focus delta.", expectedDelta0, realDelta0);
                    assertDelta1NotExecuted.run();
                    assertWfContextAfterClockworkRun(rootTask, subtasks, workItems, result, objectDelta.getOid(),
                            expectedTasks, expectedWorkItems);
                }
            }

            @Override
            protected void afterTask0Finishes(Task task, OperationResult result) throws Exception {
                assertDelta0Executed.run();
                assertDelta1NotExecuted.run();
            }

            @Override
            protected void afterRootTaskFinishes(Task task, List<Task> subtasks, OperationResult result)
                    throws Exception {
                assertDelta0Executed.run();
                if (approve) {
                    assertDelta1Executed.run();
                } else {
                    assertDelta1NotExecuted.run();
                }
            }

            @Override
            protected boolean executeImmediately() {
                return immediate;
            }

            @Override
            public List<ApprovalInstruction> getApprovalSequence() {
                return singletonList(new ApprovalInstruction(null, approve, assigneeOid, "modification comment"));
            }
        }, 1);
    }

    public <T extends ObjectType> void deleteObject(final String TEST_NAME, Class<T> clazz, String objectOid,
            boolean immediate, boolean approve, String assigneeOid, List<ExpectedTask> expectedTasks,
            List<ExpectedWorkItem> expectedWorkItems) throws Exception {

        executeTest(TEST_NAME, new TestDetails() {
            @Override
            protected LensContext createModelContext(OperationResult result) throws Exception {
                LensContext<T> lensContext = createLensContext(clazz);
                ObjectDelta<T> deleteDelta = ObjectDelta.createDeleteDelta(clazz, objectOid, prismContext);
                addFocusDeltaToContext(lensContext, deleteDelta);
                return lensContext;
            }

            @Override
            protected void afterFirstClockworkRun(Task rootTask, List<Task> subtasks, List<WorkItemType> workItems,
                    OperationResult result) throws Exception {
                if (immediate) {
                    assertFalse("There is model context in the root task (it should not be there)",
                            wfTaskUtil.hasModelContext(rootTask));
                } else {
                    ModelContext taskModelContext = wfTaskUtil.getModelContext(rootTask, result);
                    ObjectDelta realDelta0 = taskModelContext.getFocusContext().getPrimaryDelta();
                    assertTrue("Delta0 is not empty: " + realDelta0.debugDump(), realDelta0.isEmpty());
                    assertWfContextAfterClockworkRun(rootTask, subtasks, workItems, result, objectOid,
                            expectedTasks, expectedWorkItems);
                }
            }

            @Override
            protected void afterTask0Finishes(Task task, OperationResult result) throws Exception {
                assertObjectExists(clazz, objectOid);
            }

            @Override
            protected void afterRootTaskFinishes(Task task, List<Task> subtasks, OperationResult result)
                    throws Exception {
                if (approve) {
                    assertObjectDoesntExist(clazz, objectOid);
                } else {
                    assertObjectExists(clazz, objectOid);
                }
            }

            @Override
            protected boolean executeImmediately() {
                return immediate;
            }

            @Override
            protected Boolean decideOnApproval(String executionId, org.activiti.engine.task.Task task)
                    throws Exception {
                return approve;
            }
        }, 1);
    }

    protected WorkItemType getWorkItem(Task task, OperationResult result)
            throws SchemaException, SecurityViolationException, ConfigurationException, ObjectNotFoundException,
            ExpressionEvaluationException, CommunicationException {
        //Collection<SelectorOptions<GetOperationOptions>> options = GetOperationOptions.resolveItemsNamed(WorkItemType.F_TASK_REF);
        SearchResultList<WorkItemType> itemsAll = modelService.searchContainers(WorkItemType.class, null, null,
                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);
    }

    protected SearchResultList<WorkItemType> getWorkItems(Task task, OperationResult result) throws Exception {
        return modelService.searchContainers(WorkItemType.class, null, null, task, result);
    }

    protected void displayWorkItems(String title, List<WorkItemType> workItems) {
        workItems.forEach(wi -> display(title, wi));
    }

    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 abstract class TestDetails {
        protected LensContext createModelContext(OperationResult result) throws Exception {
            return null;
        }

        protected void afterFirstClockworkRun(Task rootTask, List<Task> wfSubtasks, List<WorkItemType> workItems,
                OperationResult result) throws Exception {
        }

        protected void afterTask0Finishes(Task task, OperationResult result) throws Exception {
        }

        protected void afterRootTaskFinishes(Task task, List<Task> subtasks, OperationResult result)
                throws Exception {
        }

        protected boolean executeImmediately() {
            return false;
        }

        protected Boolean decideOnApproval(String executionId, org.activiti.engine.task.Task task)
                throws Exception {
            return null;
        }

        public boolean strictlySequentialApprovals() {
            return false;
        }

        public List<ApprovalInstruction> getApprovalSequence() {
            return null;
        }
    }

    protected <F extends FocusType> void executeTest(String testName, TestDetails testDetails,
            int expectedSubTaskCount) throws Exception {

        // GIVEN
        prepareNotifications();
        dummyAuditService.clear();

        Task modelTask = taskManager.createTaskInstance(AbstractWfTestPolicy.class.getName() + "." + testName);
        modelTask.setOwner(userAdministrator);
        OperationResult result = new OperationResult("execution");

        LensContext<F> modelContext = testDetails.createModelContext(result);
        display("Model context at test start", modelContext);

        // this has problems with deleting assignments by ID
        //assertFocusModificationSanity(modelContext);

        // WHEN

        HookOperationMode mode = clockwork.run(modelContext, modelTask, result);

        // THEN

        display("Model context after first clockwork.run", modelContext);
        assertEquals("Unexpected state of the context", ModelState.PRIMARY, modelContext.getState());
        assertEquals("Wrong mode after clockwork.run in " + modelContext.getState(), HookOperationMode.BACKGROUND,
                mode);
        modelTask.refresh(result);
        display("Model task after first clockwork.run", modelTask);

        String rootTaskOid = wfTaskUtil.getRootTaskOid(modelTask);
        assertNotNull("Root task OID is not set in model task", rootTaskOid);

        Task rootTask = taskManager.getTask(rootTaskOid, result);
        display("Root task after first clockwork.run", rootTask);
        assertTrue("Root task is not persistent", rootTask.isPersistent());

        UriStack uriStack = rootTask.getOtherHandlersUriStack();
        if (!testDetails.executeImmediately()) {
            assertEquals("Invalid handler at stack position 0", ModelOperationTaskHandler.MODEL_OPERATION_TASK_URI,
                    uriStack.getUriStackEntry().get(0).getHandlerUri());
        } else {
            assertTrue("There should be no handlers for root tasks with immediate execution mode",
                    uriStack == null || uriStack.getUriStackEntry().isEmpty());
        }

        ModelContext rootModelContext = testDetails.executeImmediately() ? null
                : wfTaskUtil.getModelContext(rootTask, result);
        if (!testDetails.executeImmediately()) {
            assertNotNull("Model context is not present in root task", rootModelContext);
        } else {
            assertNull("Model context is present in root task (execution mode = immediate)", rootModelContext);
        }

        List<Task> subtasks = rootTask.listSubtasks(result);
        Task task0 = findAndRemoveTask0(subtasks, testDetails);

        assertEquals("Incorrect number of subtasks", expectedSubTaskCount, subtasks.size());

        final Collection<SelectorOptions<GetOperationOptions>> options1 = resolveItemsNamed(
                new ItemPath(T_PARENT, F_OBJECT_REF), new ItemPath(T_PARENT, F_TARGET_REF), F_ASSIGNEE_REF,
                F_ORIGINAL_ASSIGNEE_REF, new ItemPath(T_PARENT, F_REQUESTER_REF));

        List<WorkItemType> workItems = modelService.searchContainers(WorkItemType.class, null, options1, modelTask,
                result);

        testDetails.afterFirstClockworkRun(rootTask, subtasks, workItems, result);

        if (testDetails.executeImmediately()) {
            if (task0 != null) {
                waitForTaskClose(task0, 20000);
            }
            testDetails.afterTask0Finishes(rootTask, result);
        }

        for (int i = 0; i < subtasks.size(); i++) {
            Task subtask = subtasks.get(i);
            PrismProperty<ObjectTreeDeltasType> deltas = subtask.getTaskPrismObject().findProperty(
                    new ItemPath(F_WORKFLOW_CONTEXT, F_PROCESSOR_SPECIFIC_STATE, F_DELTAS_TO_PROCESS));
            assertNotNull("There are no modifications in subtask #" + i + ": " + subtask, deltas);
            assertEquals("Incorrect number of modifications in subtask #" + i + ": " + subtask, 1,
                    deltas.getRealValues().size());
            // todo check correctness of the modification?

            // now check the workflow state
            String pid = wfTaskUtil.getProcessId(subtask);
            assertNotNull("Workflow process instance id not present in subtask " + subtask, pid);

            List<org.activiti.engine.task.Task> tasks = activitiEngine.getTaskService().createTaskQuery()
                    .processInstanceId(pid).list();
            assertFalse("activiti task not found", tasks.isEmpty());

            for (org.activiti.engine.task.Task task : tasks) {
                String executionId = task.getExecutionId();
                display("Execution id = " + executionId);
                Boolean approve = testDetails.decideOnApproval(executionId, task);
                if (approve != null) {
                    workflowManager.completeWorkItem(task.getId(), approve, null, null, null, result);
                    login(userAdministrator);
                    break;
                }
            }
        }

        // alternative way of approvals executions
        if (CollectionUtils.isNotEmpty(testDetails.getApprovalSequence())) {
            List<ApprovalInstruction> instructions = new ArrayList<>(testDetails.getApprovalSequence());
            while (!instructions.isEmpty()) {
                List<WorkItemType> currentWorkItems = modelService.searchContainers(WorkItemType.class, null,
                        options1, modelTask, result);
                boolean matched = false;

                Collection<ApprovalInstruction> instructionsToConsider = testDetails.strictlySequentialApprovals()
                        ? singleton(instructions.get(0))
                        : instructions;

                main: for (ApprovalInstruction approvalInstruction : instructionsToConsider) {
                    for (WorkItemType workItem : currentWorkItems) {
                        if (approvalInstruction.matches(workItem)) {
                            if (approvalInstruction.beforeApproval != null) {
                                approvalInstruction.beforeApproval.run();
                            }
                            login(getUserFromRepo(approvalInstruction.approverOid));
                            System.out.println("Completing work item " + workItem.getExternalId() + " using "
                                    + approvalInstruction);
                            workflowManager.completeWorkItem(workItem.getExternalId(), approvalInstruction.approval,
                                    approvalInstruction.comment, null, null, result);
                            if (approvalInstruction.afterApproval != null) {
                                approvalInstruction.afterApproval.run();
                            }
                            login(userAdministrator);
                            matched = true;
                            instructions.remove(approvalInstruction);
                            break main;
                        }
                    }
                }
                if (!matched) {
                    fail("None of approval instructions " + instructionsToConsider
                            + " matched any of current work items: " + currentWorkItems);
                }
            }
        }

        waitForTaskClose(rootTask, 60000);

        subtasks = rootTask.listSubtasks(result);
        findAndRemoveTask0(subtasks, testDetails);
        testDetails.afterRootTaskFinishes(rootTask, subtasks, result);

        notificationManager.setDisabled(true);

        // Check audit
        display("Audit", dummyAuditService);
        display("Output context", modelContext);
    }

    private Task findAndRemoveTask0(List<Task> subtasks, TestDetails testDetails) {
        Task task0 = null;

        for (Task subtask : subtasks) {
            if (subtask.getTaskPrismObject().asObjectable().getWorkflowContext() == null || subtask
                    .getTaskPrismObject().asObjectable().getWorkflowContext().getProcessInstanceId() == null) {
                assertNull("More than one non-wf-monitoring subtask", task0);
                task0 = subtask;
            }
        }

        if (testDetails.executeImmediately()) {
            if (task0 != null) {
                subtasks.remove(task0);
            }
        } else {
            assertNull("Subtask for immediate execution was found even if it shouldn't be there", task0);
        }
        return task0;
    }

    protected void assertObjectInTaskTree(Task rootTask, String oid, boolean checkObjectOnSubtasks,
            OperationResult result) throws SchemaException {
        assertObjectInTask(rootTask, oid);
        if (checkObjectOnSubtasks) {
            for (Task task : rootTask.listSubtasks(result)) {
                assertObjectInTask(task, oid);
            }
        }
    }

    protected void assertObjectInTask(Task task, String oid) {
        assertEquals("Missing or wrong object OID in task " + task, oid, task.getObjectOid());
    }

    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 (Throwable e) {
                    display("Exception during task refresh", e);
                }
                OperationResult result = task.getResult();
                display("Result of timed-out task", result);
                assert false : "Timeout (" + timeout + ") while waiting for " + task + " to finish. Last result "
                        + result;
            }
        };
        IntegrationTestTools.waitFor("Waiting for " + task + " finish", checker, timeout, 1000);
    }

    protected void assertWfContextAfterClockworkRun(Task rootTask, List<Task> subtasks,
            List<WorkItemType> workItems, OperationResult result, String objectOid,
            List<ExpectedTask> expectedTasks, List<ExpectedWorkItem> expectedWorkItems) throws Exception {

        final Collection<SelectorOptions<GetOperationOptions>> options = SelectorOptions
                .createCollection(new ItemPath(F_WORKFLOW_CONTEXT, F_WORK_ITEM), createRetrieve());

        Task opTask = taskManager.createTaskInstance();
        TaskType rootTaskType = modelService.getObject(TaskType.class, rootTask.getOid(), options, opTask, result)
                .asObjectable();
        display("rootTask", rootTaskType);
        assertTrue("unexpected process instance id in root task", rootTaskType.getWorkflowContext() == null
                || rootTaskType.getWorkflowContext().getProcessInstanceId() == null);

        assertEquals("Wrong # of wf subtasks (" + expectedTasks + ")", expectedTasks.size(), subtasks.size());
        int i = 0;
        for (Task subtask : subtasks) {
            TaskType subtaskType = modelService.getObject(TaskType.class, subtask.getOid(), options, opTask, result)
                    .asObjectable();
            display("Subtask #" + (i + 1) + ": ", subtaskType);
            checkTask(subtaskType, subtask.toString(), expectedTasks.get(i));
            WfTestUtil.assertRef("requester ref", subtaskType.getWorkflowContext().getRequesterRef(),
                    USER_ADMINISTRATOR_OID, false, false);
            i++;
        }

        assertEquals("Wrong # of work items", expectedWorkItems.size(), workItems.size());
        i = 0;
        for (WorkItemType workItem : workItems) {
            display("Work item #" + (i + 1) + ": ", workItem);
            display("Task", WfContextUtil.getTask(workItem));
            if (objectOid != null) {
                WfTestUtil.assertRef("object reference", WfContextUtil.getObjectRef(workItem), objectOid, true,
                        true);
            }

            String targetOid = expectedWorkItems.get(i).targetOid;
            if (targetOid != null) {
                WfTestUtil.assertRef("target reference", WfContextUtil.getTargetRef(workItem), targetOid, true,
                        true);
            }
            WfTestUtil.assertRef("assignee reference", workItem.getOriginalAssigneeRef(),
                    expectedWorkItems.get(i).assigneeOid, false, true);
            // name is not known, as it is not stored in activiti (only OID is)
            //WfTestUtil.assertRef("task reference", workItem.getTaskRef(), null, false, true);
            final TaskType subtaskType = WfContextUtil.getTask(workItem);
            checkTask(subtaskType, "task in workItem", expectedWorkItems.get(i).task);
            WfTestUtil.assertRef("requester ref", subtaskType.getWorkflowContext().getRequesterRef(),
                    USER_ADMINISTRATOR_OID, false, true);

            i++;
        }
    }

    private void checkTask(TaskType subtaskType, String subtaskName, ExpectedTask expectedTask) {
        assertNull("Unexpected fetch result in wf subtask: " + subtaskName, subtaskType.getFetchResult());
        WfContextType wfc = subtaskType.getWorkflowContext();
        assertNotNull("Missing workflow context in wf subtask: " + subtaskName, wfc);
        assertNotNull("No process ID in wf subtask: " + subtaskName, wfc.getProcessInstanceId());
        assertEquals("Wrong process ID name in subtask: " + subtaskName, expectedTask.processName,
                wfc.getProcessInstanceName());
        if (expectedTask.targetOid != null) {
            assertEquals("Wrong target OID in subtask: " + subtaskName, expectedTask.targetOid,
                    wfc.getTargetRef().getOid());
        } else {
            assertNull("TargetRef in subtask: " + subtaskName + " present even if it shouldn't",
                    wfc.getTargetRef());
        }
        assertNotNull("Missing process start time in subtask: " + subtaskName, wfc.getStartTimestamp());
        assertNull("Unexpected process end time in subtask: " + subtaskName, wfc.getEndTimestamp());
        assertEquals("Wrong outcome", null, wfc.getOutcome());
        //assertEquals("Wrong state", null, wfc.getState());
    }

    protected String getTargetOid(String executionId) throws ConfigurationException, ObjectNotFoundException,
            SchemaException, CommunicationException, SecurityViolationException {
        LightweightObjectRef targetRef = (LightweightObjectRef) activitiEngine.getRuntimeService()
                .getVariable(executionId, CommonProcessVariableNames.VARIABLE_TARGET_REF);
        assertNotNull("targetRef not found", targetRef);
        String roleOid = targetRef.getOid();
        assertNotNull("requested role OID not found", roleOid);
        return roleOid;
    }

    protected void checkTargetOid(String executionId, String expectedOid) throws ObjectNotFoundException,
            SchemaException, CommunicationException, ConfigurationException, SecurityViolationException {
        String realOid = getTargetOid(executionId);
        assertEquals("Unexpected target OID", expectedOid, realOid);
    }

    protected abstract class TestDetails2<F extends FocusType> {
        protected PrismObject<F> getFocus(OperationResult result) throws Exception {
            return null;
        }

        protected ObjectDelta<F> getFocusDelta() throws Exception {
            return null;
        }

        protected int getNumberOfDeltasToApprove() {
            return 0;
        }

        protected List<Boolean> getApprovals() {
            return null;
        }

        protected List<ObjectDelta<F>> getExpectedDeltasToApprove() {
            return null;
        }

        protected ObjectDelta<F> getExpectedDelta0() {
            return null;
        }

        protected String getObjectOid() {
            return null;
        }

        protected List<ExpectedTask> getExpectedTasks() {
            return null;
        }

        protected List<ExpectedWorkItem> getExpectedWorkItems() {
            return null;
        }

        protected void assertDeltaExecuted(int number, boolean yes, Task rootTask, OperationResult result)
                throws Exception {
        }

        // mutually exclusive with getApprovalSequence
        protected Boolean decideOnApproval(String executionId, org.activiti.engine.task.Task task)
                throws Exception {
            return true;
        }

        private void sortSubtasks(List<Task> subtasks) {
            subtasks.sort(Comparator.comparing(this::getCompareKey));
        }

        private void sortWorkItems(List<WorkItemType> workItems) {
            workItems.sort(Comparator.comparing(this::getCompareKey));
        }

        private String getCompareKey(Task task) {
            return task.getTaskPrismObject().asObjectable().getWorkflowContext().getTargetRef().getOid();
        }

        private String getCompareKey(WorkItemType workItem) {
            return workItem.getOriginalAssigneeRef().getOid();
        }

        public List<ApprovalInstruction> getApprovalSequence() {
            return null;
        }

        protected void afterFirstClockworkRun(Task rootTask, List<Task> subtasks, List<WorkItemType> workItems,
                OperationResult result) throws Exception {
        }
    }

    protected <F extends FocusType> void executeTest2(String testName, TestDetails2<F> testDetails2,
            int expectedSubTaskCount, boolean immediate) throws Exception {
        executeTest(testName, new TestDetails() {
            @Override
            protected LensContext<F> createModelContext(OperationResult result) throws Exception {
                PrismObject<F> focus = testDetails2.getFocus(result);
                // TODO "object create" context
                LensContext<F> lensContext = createLensContext(focus.getCompileTimeClass());
                fillContextWithFocus(lensContext, focus);
                addFocusDeltaToContext(lensContext, testDetails2.getFocusDelta());
                if (immediate) {
                    lensContext.setOptions(ModelExecuteOptions.createExecuteImmediatelyAfterApproval());
                }
                return lensContext;
            }

            @Override
            protected void afterFirstClockworkRun(Task rootTask, List<Task> subtasks, List<WorkItemType> workItems,
                    OperationResult result) throws Exception {
                if (immediate) {
                    assertFalse("There is model context in the root task (it should not be there)",
                            wfTaskUtil.hasModelContext(rootTask));
                } else {
                    ModelContext taskModelContext = wfTaskUtil.getModelContext(rootTask, result);
                    ObjectDelta expectedDelta0 = testDetails2.getExpectedDelta0();
                    ObjectDelta realDelta0 = taskModelContext.getFocusContext().getPrimaryDelta();
                    assertDeltasEqual("Wrong delta left as primary focus delta. ", expectedDelta0, realDelta0);
                    for (int i = 0; i <= testDetails2.getNumberOfDeltasToApprove(); i++) {
                        testDetails2.assertDeltaExecuted(i, false, rootTask, result);
                    }
                    testDetails2.sortSubtasks(subtasks);
                    testDetails2.sortWorkItems(workItems);
                    assertWfContextAfterClockworkRun(rootTask, subtasks, workItems, result,
                            testDetails2.getObjectOid(), testDetails2.getExpectedTasks(),
                            testDetails2.getExpectedWorkItems());
                    for (Task subtask : subtasks) {
                        if (subtask.getWorkflowContext() != null
                                && subtask.getWorkflowContext().getProcessInstanceId() != null) {
                            Task opTask = taskManager.createTaskInstance("afterFirstClockworkRun");
                            OperationResult opResult = opTask.getResult();
                            ApprovalSchemaExecutionInformationType info = workflowManager
                                    .getApprovalSchemaExecutionInformation(subtask.getOid(), opTask, opResult);
                            display("Execution info for " + subtask, info);
                            opResult.computeStatus();
                            assertSuccess(
                                    "Unexpected problem when looking at getApprovalSchemaExecutionInformation result",
                                    opResult);
                        }
                    }
                }
                testDetails2.afterFirstClockworkRun(rootTask, subtasks, workItems, result);
            }

            @Override
            protected void afterTask0Finishes(Task task, OperationResult result) throws Exception {
                if (!immediate) {
                    return;
                }
                for (int i = 1; i <= testDetails2.getNumberOfDeltasToApprove(); i++) {
                    testDetails2.assertDeltaExecuted(i, false, task, result);
                }
                testDetails2.assertDeltaExecuted(0, true, task, result);
            }

            @Override
            protected void afterRootTaskFinishes(Task task, List<Task> subtasks, OperationResult result)
                    throws Exception {
                for (int i = 0; i <= testDetails2.getNumberOfDeltasToApprove(); i++) {
                    testDetails2.assertDeltaExecuted(i, i == 0 || testDetails2.getApprovals().get(i - 1), task,
                            result);
                }
            }

            @Override
            protected boolean executeImmediately() {
                return immediate;
            }

            @Override
            protected Boolean decideOnApproval(String executionId, org.activiti.engine.task.Task task)
                    throws Exception {
                return testDetails2.decideOnApproval(executionId, task);
            }

            @Override
            public List<ApprovalInstruction> getApprovalSequence() {
                return testDetails2.getApprovalSequence();
            }
        }, expectedSubTaskCount);
    }

    protected void assertDeltasEqual(String message, ObjectDelta expectedDelta, ObjectDelta realDelta) {
        //      removeOldValues(expectedDelta);
        //      removeOldValues(realDelta);
        if (!expectedDelta.equivalent(realDelta)) {
            fail(message + "\nExpected:\n" + expectedDelta.debugDump() + "\nReal:\n" + realDelta.debugDump());
        }
    }

    //   private void removeOldValues(ObjectDelta<?> delta) {
    //      if (delta.isModify()) {
    //         delta.getModifications().forEach(mod -> mod.setEstimatedOldValues(null));
    //      }
    //   }

    protected void assertNoObject(ObjectType object)
            throws SchemaException, ObjectNotFoundException, SecurityViolationException, CommunicationException,
            ConfigurationException, ExpressionEvaluationException {
        assertNull("Object was created but it shouldn't be",
                searchObjectByName(object.getClass(), object.getName().getOrig()));
    }

    protected void assertNoObject(PrismObject<? extends ObjectType> object)
            throws SchemaException, ObjectNotFoundException, SecurityViolationException, CommunicationException,
            ConfigurationException, ExpressionEvaluationException {
        assertNoObject(object.asObjectable());
    }

    protected <T extends ObjectType> void assertObject(T object)
            throws SchemaException, ObjectNotFoundException, SecurityViolationException, CommunicationException,
            ConfigurationException, ExpressionEvaluationException {
        PrismObject<T> objectFromRepo = searchObjectByName((Class<T>) object.getClass(),
                object.getName().getOrig());
        assertNotNull("Object " + object + " was not created", objectFromRepo);
        objectFromRepo.removeItem(new ItemPath(ObjectType.F_METADATA), Item.class);
        objectFromRepo.removeItem(new ItemPath(ObjectType.F_OPERATION_EXECUTION), Item.class);
        assertEquals("Object is different from the one that was expected", object, objectFromRepo.asObjectable());
    }

    protected void checkVisibleWorkItem(ExpectedWorkItem expectedWorkItem, int count, Task task,
            OperationResult result) throws SchemaException, ObjectNotFoundException, ConfigurationException,
            SecurityViolationException, ExpressionEvaluationException, CommunicationException {
        S_AtomicFilterExit q = QueryUtils.filterForAssignees(
                QueryBuilder.queryFor(WorkItemType.class, prismContext), SecurityUtil.getPrincipal(),
                OtherPrivilegesLimitationType.F_APPROVAL_WORK_ITEMS);
        List<WorkItemType> currentWorkItems = modelService.searchContainers(WorkItemType.class, q.build(), null,
                task, result);
        long found = currentWorkItems.stream()
                .filter(wi -> expectedWorkItem == null || expectedWorkItem.matches(wi)).count();
        assertEquals("Wrong # of matching work items", count, found);
    }

}