org.bonitasoft.engine.process.ProcessExecutionIT.java Source code

Java tutorial

Introduction

Here is the source code for org.bonitasoft.engine.process.ProcessExecutionIT.java

Source

/**
 * Copyright (C) 2015 BonitaSoft S.A.
 * BonitaSoft, 32 rue Gustave Eiffel - 38000 Grenoble
 * This library is free software; you can redistribute it and/or modify it under the terms
 * of the GNU Lesser General Public License as published by the Free Software Foundation
 * version 2.1 of the License.
 * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Lesser General Public License for more details.
 * You should have received a copy of the GNU Lesser General Public License along with this
 * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
 * Floor, Boston, MA 02110-1301, USA.
 **/
package org.bonitasoft.engine.process;

import static org.assertj.core.api.Assertions.assertThat;
import static org.bonitasoft.engine.test.BuildTestUtil.generateConnectorImplementation;
import static org.bonitasoft.engine.test.BuildTestUtil.generateJarAndBuildBarResource;
import static org.junit.Assert.*;

import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.List;

import org.apache.commons.lang3.time.DateUtils;
import org.bonitasoft.engine.TestWithUser;
import org.bonitasoft.engine.bpm.bar.BusinessArchive;
import org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder;
import org.bonitasoft.engine.bpm.comment.Comment;
import org.bonitasoft.engine.bpm.comment.SearchCommentsDescriptor;
import org.bonitasoft.engine.bpm.connector.ConnectorEvent;
import org.bonitasoft.engine.bpm.flownode.ActivityInstance;
import org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstance;
import org.bonitasoft.engine.bpm.flownode.ArchivedFlowNodeInstance;
import org.bonitasoft.engine.bpm.flownode.ArchivedFlowNodeInstanceSearchDescriptor;
import org.bonitasoft.engine.bpm.flownode.HumanTaskInstance;
import org.bonitasoft.engine.bpm.flownode.HumanTaskInstanceSearchDescriptor;
import org.bonitasoft.engine.bpm.flownode.UserTaskInstance;
import org.bonitasoft.engine.bpm.process.ArchivedProcessInstance;
import org.bonitasoft.engine.bpm.process.ArchivedProcessInstanceNotFoundException;
import org.bonitasoft.engine.bpm.process.DesignProcessDefinition;
import org.bonitasoft.engine.bpm.process.ProcessDefinition;
import org.bonitasoft.engine.bpm.process.ProcessInstance;
import org.bonitasoft.engine.bpm.process.ProcessInstanceNotFoundException;
import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;
import org.bonitasoft.engine.exception.DeletionException;
import org.bonitasoft.engine.exception.SearchException;
import org.bonitasoft.engine.exception.UpdateException;
import org.bonitasoft.engine.expression.ExpressionBuilder;
import org.bonitasoft.engine.identity.User;
import org.bonitasoft.engine.search.Order;
import org.bonitasoft.engine.search.SearchOptions;
import org.bonitasoft.engine.search.SearchOptionsBuilder;
import org.bonitasoft.engine.search.SearchResult;
import org.bonitasoft.engine.test.APITestUtil;
import org.bonitasoft.engine.test.BuildTestUtil;
import org.bonitasoft.engine.test.TestStates;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ProcessExecutionIT extends TestWithUser {

    @Rule
    public ExpectedException expectedException = ExpectedException.none();

    private static final Logger LOGGER = LoggerFactory.getLogger(ProcessExecutionIT.class);

    /**
     * there was an issue on deploy when the classloader needed to be refreshed
     * (because of Schemafactory was loading parser not in transaction)
     *
     * @throws Exception
     */
    @Test
    public void ensureADeployWorksAfterAChangeInDependencies() throws Exception {
        final DesignProcessDefinition designProcessDefinition1 = BuildTestUtil
                .buildProcessDefinitionWithHumanAndAutomaticSteps("My_Process123", "1.0", Arrays.asList("step1"),
                        Arrays.asList(true));
        final ProcessDefinition processDefinition1 = deployAndEnableProcessWithActor(designProcessDefinition1,
                ACTOR_NAME, user);
        getCommandAPI().addDependency("kikoo", new byte[] { 0, 2, 3 });
        final DesignProcessDefinition designProcessDefinition = BuildTestUtil
                .buildProcessDefinitionWithHumanAndAutomaticSteps("My_Process", "1.0", Arrays.asList("step1"),
                        Arrays.asList(true));
        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition,
                ACTOR_NAME, user);
        disableAndDeleteProcess(processDefinition);
        disableAndDeleteProcess(processDefinition1);
        getCommandAPI().removeDependency("kikoo");
    }

    @Test
    public void startProcessWithCurrentUser() throws Exception {
        final DesignProcessDefinition designProcessDefinition = BuildTestUtil
                .buildProcessDefinitionWithHumanAndAutomaticSteps("My_Process", "1.0", Arrays.asList("step1"),
                        Arrays.asList(true));
        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition,
                ACTOR_NAME, user);
        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());
        // Check that the current getSession() user name is the one used to start the process:
        LOGGER.debug("current getSession() user name used to start the process: " + processInstance.getStartedBy());
        assertEquals(getSession().getUserId(), processInstance.getStartedBy());

        // Clean up
        waitForUserTask(processInstance, "step1");
        disableAndDeleteProcess(processDefinition);
    }

    @Test
    public void startProcessFor() throws Exception {
        final User jack = createUser("jack", PASSWORD);

        final DesignProcessDefinition designProcessDefinition = BuildTestUtil
                .buildProcessDefinitionWithHumanAndAutomaticSteps("My_Process", "1.0", Arrays.asList("step1"),
                        Arrays.asList(true));
        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition,
                ACTOR_NAME, user);
        final ProcessInstance processInstance = getProcessAPI().startProcess(jack.getId(),
                processDefinition.getId());

        try {
            waitForUserTask(processInstance, "step1");
            // Check that the given user name is the one used to start the process:
            assertEquals(jack.getId(), processInstance.getStartedBy());
            assertEquals(user.getId(), processInstance.getStartedBySubstitute());

            // Check system comment
            final SearchOptions searchOptions = new SearchOptionsBuilder(0, 100)
                    .filter(SearchCommentsDescriptor.PROCESS_INSTANCE_ID, processInstance.getId()).done();
            final List<Comment> comments = getProcessAPI().searchComments(searchOptions).getResult();
            boolean haveCommentForDelegate = false;
            for (final Comment comment : comments) {
                haveCommentForDelegate = haveCommentForDelegate || comment.getContent().contains(
                        "The user " + USERNAME + " acting as delegate of the user jack has started the case.");
            }
            assertTrue(haveCommentForDelegate);
        } finally {
            // Clean up
            disableAndDeleteProcess(processDefinition);
            deleteUsers(jack);
        }
    }

    @Test
    public void createAndExecuteProcessWithAutomaticSteps() throws Exception {
        final DesignProcessDefinition designProcessDefinition = BuildTestUtil
                .buildProcessDefinitionWithHumanAndAutomaticSteps("My_Process", "1.1",
                        Arrays.asList("step1", "step2"), Arrays.asList(false, false));

        final ProcessDefinition processDefinition = deployAndEnableProcess(designProcessDefinition);
        final ProcessInstance processInstance = getProcessAPI().startProcess(user.getId(),
                processDefinition.getId());
        waitForProcessToFinish(processInstance);

        disableAndDeleteProcess(processDefinition);
    }

    @Test
    public void deleteUnknownProcess() throws Exception {
        expectedException.expect(DeletionException.class);
        getProcessAPI().deleteProcessDefinition(123456789);
    }

    @Test
    public void executeProcessWithNoActivities() throws Exception {
        final DesignProcessDefinition designProcessDefinition = BuildTestUtil
                .buildProcessDefinitionWithHumanAndAutomaticSteps("My_Process", "1.3",
                        Collections.<String>emptyList(), Collections.<Boolean>emptyList());
        final ProcessDefinition processDefinition = deployAndEnableProcess(designProcessDefinition);
        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());

        final List<ActivityInstance> activities = getProcessAPI().getActivities(processDefinition.getId(), 0, 200);
        assertEquals(0, activities.size());
        assertTrue("Process instance should be completed",
                containsState(getProcessAPI().getArchivedProcessInstances(processInstance.getId(), 0, 10),
                        TestStates.NORMAL_FINAL));// FIXME

        disableAndDeleteProcess(processDefinition);
    }

    @Test
    public void checkStartAndEndDate() throws Exception {
        final DesignProcessDefinition designProcessDefinition = BuildTestUtil
                .buildProcessDefinitionWithHumanAndAutomaticSteps("My_ProcessToCheckDate", "1.0",
                        Arrays.asList("step1"), Arrays.asList(true));
        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition,
                ACTOR_NAME, user);

        long before = new Date().getTime();
        Thread.sleep(10);
        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());
        Thread.sleep(10);
        long after = new Date().getTime();
        final long startDate = processInstance.getStartDate().getTime();
        assertTrue("The process instance must start between " + before + " and " + after + ", but was " + startDate,
                after >= startDate && startDate >= before);
        assertEquals(getSession().getUserId(), processInstance.getStartedBy());

        final Long step1Id = waitForUserTask("step1");
        before = new Date().getTime();
        assignAndExecuteStep(step1Id, user);
        waitForProcessToFinish(processInstance);
        after = new Date().getTime();
        final long endDate = getProcessAPI().getFinalArchivedProcessInstance(processInstance.getId()).getEndDate()
                .getTime();
        assertTrue("The process instance must finish between " + before + " and " + after + ", but was " + endDate,
                after >= endDate && endDate >= before);

        disableAndDeleteProcess(processDefinition);
    }

    @Test
    public void checkLastUpdateDateOfAnArchivedProcess() throws Exception {
        final DesignProcessDefinition designProcessDefinition = BuildTestUtil
                .buildProcessDefinitionWithHumanAndAutomaticSteps("My_ProcessToCheckDate", "1.0",
                        Arrays.asList("step1"), Arrays.asList(true));
        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition,
                ACTOR_NAME, user);

        long before = new Date().getTime();
        Thread.sleep(10);
        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());
        Thread.sleep(10);
        long after = new Date().getTime();
        final long processStartDate = processInstance.getStartDate().getTime();
        assertTrue(
                "The process instance " + processInstance.getName() + " must start between <" + before + "> and <"
                        + after + ">, but was <" + processStartDate + ">",
                after >= processStartDate && processStartDate >= before);
        assertEquals(getSession().getUserId(), processInstance.getStartedBy());

        final Long step1Id = waitForUserTask("step1");
        before = new Date().getTime();
        assignAndExecuteStep(step1Id, user);
        waitForProcessToFinish(processInstance);
        after = new Date().getTime();
        final long lastUpdate = getProcessAPI().getFinalArchivedProcessInstance(processInstance.getId())
                .getLastUpdate().getTime();
        assertTrue(
                "The process instance " + processInstance.getName() + " must update in last between <" + before
                        + "> and <" + after + ">, but was <" + lastUpdate + ">",
                after >= lastUpdate && lastUpdate >= before);

        disableAndDeleteProcess(processDefinition);
    }

    @Test
    public void checkProcessIsArchived() throws Exception {
        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME,
                PROCESS_VERSION);
        builder.addActor(ACTOR_NAME);
        builder.addStartEvent("start");
        builder.addUserTask("step1", ACTOR_NAME);
        builder.addEndEvent("end");
        builder.addTransition("start", "step1").addTransition("step1", "end");

        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME,
                user);
        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());
        final Long step1Id = waitForUserTask(processInstance, "step1");

        final List<ArchivedProcessInstance> archs = getProcessAPI()
                .getArchivedProcessInstances(processInstance.getId(), 0, 100);
        assertEquals(1, archs.size());
        assertEquals(TestStates.INITIALIZING.getStateName(), archs.get(0).getState());

        assignAndExecuteStep(step1Id, user);
        waitForProcessToFinish(processInstance);

        // Verify if the process instance is archived
        final ArchivedProcessInstance archivedProcessInstance = getProcessAPI()
                .getFinalArchivedProcessInstance(processInstance.getId());
        assertNotNull(archivedProcessInstance);
        try {
            getProcessAPI().getProcessInstance(processInstance.getId());
            fail("A ProcessInstanceNotFoundException should have been raised");
        } catch (final ProcessInstanceNotFoundException e) {
            // ok
        }

        // Verify if the flow node instances are archived
        final SearchOptionsBuilder optionsBuilder = new SearchOptionsBuilder(0, 20);
        optionsBuilder.sort(ArchivedFlowNodeInstanceSearchDescriptor.NAME, Order.ASC);
        optionsBuilder.filter(ArchivedFlowNodeInstanceSearchDescriptor.STATE_NAME, "completed");
        final List<ArchivedFlowNodeInstance> archivedFlowNodeInstances = getProcessAPI()
                .searchArchivedFlowNodeInstances(optionsBuilder.done()).getResult();
        // To uncomment if need to fix BS-11970
        //        assertEquals(3, archivedFlowNodeInstances.size());
        //        assertEquals("end", archivedFlowNodeInstances.get(0).getName());
        //        assertEquals("start", archivedFlowNodeInstances.get(1).getName());
        //        assertEquals("step1", archivedFlowNodeInstances.get(2).getName());
        // To remove if need to fix BS-11970
        assertEquals(1, archivedFlowNodeInstances.size());
        assertEquals("step1", archivedFlowNodeInstances.get(0).getName());

        disableAndDeleteProcess(processDefinition);
    }

    @Test
    public void getArchivedProcessInstanceById() throws Exception {
        final DesignProcessDefinition designProcessDefinition = BuildTestUtil
                .buildProcessDefinitionWithHumanAndAutomaticSteps("ProcessToArchive", "1.0", Arrays.asList("step1"),
                        Arrays.asList(true));
        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition,
                APITestUtil.ACTOR_NAME, user);
        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());
        waitForUserTask(processInstance, "step1");
        final List<ArchivedProcessInstance> archs = getProcessAPI()
                .getArchivedProcessInstances(processInstance.getId(), 0, 100);
        assertEquals(1, archs.size());
        final ArchivedProcessInstance archivedProcessInstance = archs.get(0);
        assertEquals(archivedProcessInstance,
                getProcessAPI().getArchivedProcessInstance(archivedProcessInstance.getId()));
        disableAndDeleteProcess(processDefinition);
    }

    @Test(expected = ArchivedProcessInstanceNotFoundException.class)
    public void getArchivedProcessInstanceByIdNotFound() throws Exception {
        getProcessAPI().getArchivedProcessInstance(123456789L);
    }

    @Test
    public void checkArchiveDate() throws Exception {
        final DesignProcessDefinition designProcessDefinition = BuildTestUtil
                .buildProcessDefinitionWithHumanAndAutomaticSteps("My_ProcessToCheckDate", "1.0",
                        Arrays.asList("step1"), Arrays.asList(true));
        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition,
                ACTOR_NAME, user);

        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());
        final long step1Id = waitForUserTask(processInstance, "step1");
        final long before = new Date().getTime();
        assignAndExecuteStep(step1Id, user.getId());
        waitForProcessToFinish(processInstance);
        final long after = new Date().getTime();
        final ArchivedProcessInstance archivedProcessInstance = getProcessAPI()
                .getFinalArchivedProcessInstance(processInstance.getId());
        assertNotNull(archivedProcessInstance);
        long archiveDate = archivedProcessInstance.getArchiveDate().getTime();
        assertTrue("The process must be archived between " + before + " and " + after + ", but was " + archiveDate,
                after >= archiveDate && archiveDate >= before);

        final ArchivedActivityInstance archivedActivityInstance = getProcessAPI()
                .getArchivedActivityInstance(step1Id);
        assertNotNull(archivedActivityInstance);
        assertEquals(step1Id, archivedActivityInstance.getSourceObjectId());
        archiveDate = archivedActivityInstance.getArchiveDate().getTime();
        assertTrue("The step1 must be archived between " + before + " and " + after + ", but was " + archiveDate,
                after >= archiveDate && archiveDate >= before);

        disableAndDeleteProcess(processDefinition);
    }

    @Test
    public void checkArchiveStartedBy() throws Exception {
        final DesignProcessDefinition designProcessDefinition = BuildTestUtil
                .buildProcessDefinitionWithHumanAndAutomaticSteps("My_ProcessToCheckDate", "1.0",
                        Arrays.asList("step1"), Arrays.asList(true));
        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition,
                ACTOR_NAME, user);

        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());
        assertEquals(user.getId(), processInstance.getStartedBy());

        waitForUserTaskAndExecuteIt(processInstance, "step1", user);
        waitForProcessToFinish(processInstance);
        final ArchivedProcessInstance archivedProcessInstance = getProcessAPI()
                .getFinalArchivedProcessInstance(processInstance.getId());
        assertNotNull(archivedProcessInstance);
        assertEquals(user.getId(), archivedProcessInstance.getStartedBy());

        disableAndDeleteProcess(processDefinition);
    }

    @Test
    public void activityDisplayDescriptionUndefined() throws Exception {
        // create process definition;
        final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder()
                .createNewInstance(PROCESS_NAME, PROCESS_VERSION).addActor(ACTOR_NAME).addAutomaticTask("auto1")
                .addUserTask("task1", ACTOR_NAME).getProcess();
        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition,
                ACTOR_NAME, user);
        final ProcessInstance pi = getProcessAPI().startProcess(processDefinition.getId());

        final ArchivedActivityInstance auto1 = waitForActivityInCompletedState(pi, "auto1", true);
        assertEquals(null, auto1.getDisplayDescription());

        final ActivityInstance activityInstance = waitForTaskInState(pi, "task1", TestStates.READY);
        assertEquals(null, activityInstance.getDisplayDescription());

        disableAndDeleteProcess(processDefinition);
    }

    @Test
    public void should_update_and_sort_due_date_with_null_values() throws Exception {
        //given
        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder()
                .createNewInstance("processWithNullDueDate", "7.4");
        builder.addActor(ACTOR_NAME);
        builder.addUserTask("step1", ACTOR_NAME).addExpectedDuration(3600L);
        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME,
                user);
        final ProcessInstance processInstance1 = getProcessAPI().startProcess(processDefinition.getId());
        final ProcessInstance processInstance2 = getProcessAPI().startProcess(processDefinition.getId());
        final ProcessInstance processInstance3 = getProcessAPI().startProcess(processDefinition.getId());
        final UserTaskInstance todayTask = (UserTaskInstance) waitForUserTaskAndAssignIt(processInstance1, "step1",
                user);
        final UserTaskInstance nextWeekTask = (UserTaskInstance) waitForUserTaskAndAssignIt(processInstance2,
                "step1", user);
        final UserTaskInstance nullDueDate = (UserTaskInstance) waitForUserTaskAndAssignIt(processInstance3,
                "step1", user);

        //when
        getProcessAPI().updateDueDateOfTask(nullDueDate.getId(), null);
        final Date nextWeekDueDate = DateUtils.addDays(nextWeekTask.getExpectedEndDate(), 7);
        getProcessAPI().updateDueDateOfTask(nextWeekTask.getId(), nextWeekDueDate);

        //new business logic for sort to be used in portal
        final SearchResult<HumanTaskInstance> ascNullsLast = getHumanTaskInstanceSearchResult(Order.ASC_NULLS_LAST);
        final SearchResult<HumanTaskInstance> descNullsFirst = getHumanTaskInstanceSearchResult(
                Order.DESC_NULLS_FIRST);

        //not required by business logic but tested to verify orderBy sql keyword are valid SQL
        final SearchResult<HumanTaskInstance> ascNullsFirst = getHumanTaskInstanceSearchResult(
                Order.ASC_NULLS_FIRST);
        final SearchResult<HumanTaskInstance> descNullsLast = getHumanTaskInstanceSearchResult(
                Order.DESC_NULLS_LAST);

        //then
        assertThat(ascNullsLast.getResult()).extracting("id").as("should have null as last value")
                .containsExactly(todayTask.getId(), nextWeekTask.getId(), nullDueDate.getId());
        assertThat(ascNullsFirst.getResult()).extracting("id").as("should have null as first value")
                .containsExactly(nullDueDate.getId(), todayTask.getId(), nextWeekTask.getId());
        assertThat(descNullsFirst.getResult()).extracting("id").as("should have null as first value")
                .containsExactly(nullDueDate.getId(), nextWeekTask.getId(), todayTask.getId());
        assertThat(descNullsLast.getResult()).extracting("id").as("should have null as first value")
                .containsExactly(nextWeekTask.getId(), todayTask.getId(), nullDueDate.getId());

        assertThat(getProcessAPI().getHumanTaskInstance(nullDueDate.getId()).getExpectedEndDate())
                .as("should have updated expected date to null").isNull();
        assertThat(getProcessAPI().getHumanTaskInstance(nextWeekTask.getId()).getExpectedEndDate())
                .as("should have updated expected date to next week").isEqualTo(nextWeekDueDate);

        disableAndDeleteProcess(processDefinition);
    }

    public SearchResult<HumanTaskInstance> getHumanTaskInstanceSearchResult(Order order) throws SearchException {
        return getProcessAPI().searchHumanTaskInstances(
                new SearchOptionsBuilder(0, 100).sort(HumanTaskInstanceSearchDescriptor.DUE_DATE, order).done());
    }

    @Test(expected = UpdateException.class)
    public void updateDueDateOfUnknownTask() throws Exception {
        getProcessAPI().updateDueDateOfTask(123456789L, new Date());
    }

    @Test
    public void should_be_able_to_set_due_date_with_connector() throws Exception {
        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder()
                .createNewInstance("processWithDueDate", "1.0");
        builder.addActor(ACTOR_NAME);
        builder.addUserTask("step1", ACTOR_NAME).addConnector("setDueDate", "dueDateConnector", "1.0",
                ConnectorEvent.ON_ENTER);

        BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive()
                .setProcessDefinition(builder.done())
                .addConnectorImplementation(
                        generateConnectorImplementation("dueDateConnector", "1.0", SetDueDateConnector.class))
                .addClasspathResource(generateJarAndBuildBarResource(SetDueDateConnector.class, "dueDate.jar"))
                .done();

        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(businessArchive, ACTOR_NAME,
                user);
        final ProcessInstance processInstance1 = getProcessAPI().startProcess(processDefinition.getId());
        final HumanTaskInstance step1 = waitForUserTaskAndGetIt(processInstance1, "step1");

        assertThat(step1.getExpectedEndDate()).isEqualTo(SetDueDateConnector.dueDate);

        disableAndDeleteProcess(processDefinition);
    }

    @Test
    public void executeTaskFor() throws Exception {
        final DesignProcessDefinition designProcessDefinition = BuildTestUtil
                .buildProcessDefinitionWithHumanAndAutomaticSteps("My_Process", "1.0",
                        Arrays.asList("step1", "step2"), Arrays.asList(true, true));
        final User jack = createUser("jack", PASSWORD);
        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition,
                APITestUtil.ACTOR_NAME, user);
        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());

        try {
            // execute step 1 using john
            final ActivityInstance step1 = waitForUserTaskAndGetIt(processInstance, "step1");
            assertEquals(0, step1.getExecutedBy());

            getProcessAPI().assignUserTask(step1.getId(), jack.getId());
            getProcessAPI().executeFlowNode(jack.getId(), step1.getId());
            waitForUserTask(processInstance, "step2");

            // check that the step1 was executed by john
            final ArchivedActivityInstance step1Archived = getProcessAPI()
                    .getArchivedActivityInstance(step1.getId());
            assertEquals(jack.getId(), step1Archived.getExecutedBy());
            assertEquals(user.getId(), step1Archived.getExecutedBySubstitute());

            // Check system comment
            final SearchOptions searchOptions = new SearchOptionsBuilder(0, 100)
                    .filter(SearchCommentsDescriptor.PROCESS_INSTANCE_ID, processInstance.getId()).done();
            final List<Comment> comments = getProcessAPI().searchComments(searchOptions).getResult();
            boolean haveCommentForDelegate = false;
            for (final Comment comment : comments) {
                haveCommentForDelegate = haveCommentForDelegate || comment.getContent().contains("The user "
                        + USERNAME + " acting as delegate of the user jack has done the task \"step1\".");
            }
            assertTrue(haveCommentForDelegate);
        } finally {
            // clean
            disableAndDeleteProcess(processDefinition);
            deleteUsers(jack);
        }
    }

    @Test
    public void systemCommentsShouldBeAutoAddedAtTaskAssignment() throws Exception {
        final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder()
                .createNewInstance("ProcessToArchive", "1.0");
        builder.addActor(ACTOR_NAME);
        builder.addUserTask("step1", ACTOR_NAME)
                .addDisplayName(new ExpressionBuilder().createConstantStringExpression("Step1 display name"));
        builder.addUserTask("step2", ACTOR_NAME);
        builder.addTransition("step1", "step2");
        final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(),
                APITestUtil.ACTOR_NAME, user);
        final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId());
        waitForUserTaskAndExecuteIt(processInstance, "step1", user);
        waitForUserTask(processInstance, "step2");

        final SearchResult<Comment> searchResult = getProcessAPI()
                .searchComments(new SearchOptionsBuilder(0, 5).done());
        final List<Comment> commentList = searchResult.getResult();
        assertEquals(1, commentList.size());
        assertEquals("The task \"Step1 display name\" is now assigned to " + USERNAME,
                commentList.get(0).getContent());

        assertEquals(1, getProcessAPI().countComments(new SearchOptionsBuilder(0, 5).done()));

        disableAndDeleteProcess(processDefinition);
    }

}