/** starting after a task should not respect that tasks asyncAfter setting */
  @Deployment
  public void testStartAfterAsync() {
    ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("exclusiveGateway");
    String processInstanceId = processInstance.getId();

    runtimeService
        .createProcessInstanceModification(processInstance.getId())
        .startAfterActivity("task2")
        .execute();

    // there is now a job for the end event after task2
    Job job = managementService.createJobQuery().singleResult();
    assertNotNull(job);

    Execution jobExecution =
        runtimeService
            .createExecutionQuery()
            .activityId("end2")
            .executionId(job.getExecutionId())
            .singleResult();
    assertNotNull(jobExecution);

    // end process
    completeTasksInOrder("task1");
    managementService.executeJob(job.getId());
    assertProcessEnded(processInstanceId);
  }
  @Deployment(resources = NESTED_PARALLEL_ASYNC_BEFORE_SCOPE_TASK_PROCESS)
  public void testCancelNestedConcurrentTransitionInstance() {
    // given a process instance with an instance of outerTask and two asynchronous tasks nested
    // in a subprocess
    ProcessInstance processInstance =
        runtimeService.startProcessInstanceByKey("nestedConcurrentTasksProcess");
    String processInstanceId = processInstance.getId();

    // when one of the inner transition instances is cancelled
    ActivityInstance tree = runtimeService.getActivityInstance(processInstanceId);

    runtimeService
        .createProcessInstanceModification(processInstance.getId())
        .cancelTransitionInstance(
            getChildTransitionInstanceForTargetActivity(tree, "innerTask1").getId())
        .execute();

    // then the activity instance and execution trees should match
    ActivityInstance updatedTree = runtimeService.getActivityInstance(processInstanceId);
    assertThat(updatedTree)
        .hasStructure(
            describeActivityInstanceTree(processInstance.getProcessDefinitionId())
                .activity("outerTask")
                .beginScope("subProcess")
                .transition("innerTask2")
                .done());

    ExecutionTree executionTree =
        ExecutionTree.forExecution(processInstance.getId(), processEngine);

    assertThat(executionTree)
        .matches(
            describeExecutionTree(null)
                .scope()
                .child("outerTask")
                .concurrent()
                .noScope()
                .up()
                .child(null)
                .concurrent()
                .noScope()
                .child("innerTask2")
                .scope()
                .done());

    // and the job for innerTask2 should still be there and assigned to the correct execution
    Job innerTask2Job = managementService.createJobQuery().singleResult();
    assertNotNull(innerTask2Job);

    Execution innerTask2Execution =
        runtimeService.createExecutionQuery().activityId("innerTask2").singleResult();
    assertNotNull(innerTask2Execution);

    assertEquals(innerTask2Job.getExecutionId(), innerTask2Execution.getId());

    // and completing the process should succeed
    completeTasksInOrder("outerTask");
    managementService.executeJob(innerTask2Job.getId());
    completeTasksInOrder("innerTask2");

    assertProcessEnded(processInstanceId);
  }