content: /*
   * Copyright 2010-2025 Hyland Software, Inc. and its affiliates.
   *
   * 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 org.activiti.engine.test.history;
  
  import static java.util.Arrays.asList;
  import static java.util.Collections.singletonList;
  import static org.assertj.core.api.Assertions.assertThat;
  import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
  
  import java.util.ArrayList;
  import java.util.Calendar;
  import java.util.Date;
  import java.util.GregorianCalendar;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  import org.activiti.engine.ActivitiIllegalArgumentException;
  import org.activiti.engine.history.HistoricIdentityLink;
  import org.activiti.engine.history.HistoricProcessInstance;
  import org.activiti.engine.impl.history.HistoryLevel;
  import org.activiti.engine.impl.test.PluggableActivitiTestCase;
  import org.activiti.engine.runtime.ProcessInstance;
  import org.activiti.engine.runtime.ProcessInstanceBuilder;
  import org.activiti.engine.task.Task;
  import org.activiti.engine.test.Deployment;
  
  public class HistoricProcessInstanceTest extends PluggableActivitiTestCase {
  
      @Deployment(resources = { "org/activiti/engine/test/history/oneTaskProcess.bpmn20.xml" })
      public void testHistoricDataCreatedForProcessExecution() {
          Calendar calendar = new GregorianCalendar();
          calendar.set(Calendar.YEAR, 2010);
          calendar.set(Calendar.MONTH, 8);
          calendar.set(Calendar.DAY_OF_MONTH, 30);
          calendar.set(Calendar.HOUR_OF_DAY, 12);
          calendar.set(Calendar.MINUTE, 0);
          calendar.set(Calendar.SECOND, 0);
          calendar.set(Calendar.MILLISECOND, 0);
          Date noon = calendar.getTime();
  
          processEngineConfiguration.getClock().setCurrentTime(noon);
          final ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(
              "oneTaskProcess",
              "myBusinessKey"
          );
  
          assertThat(historyService.createHistoricProcessInstanceQuery().unfinished().count()).isEqualTo(1);
          assertThat(historyService.createHistoricProcessInstanceQuery().finished().count()).isEqualTo(0);
          HistoricProcessInstance historicProcessInstance = historyService
              .createHistoricProcessInstanceQuery()
              .processInstanceId(processInstance.getId())
              .singleResult();
  
          assertThat(historicProcessInstance).isNotNull();
          assertThat(historicProcessInstance.getId()).isEqualTo(processInstance.getId());
          assertThat(historicProcessInstance.getBusinessKey()).isEqualTo(processInstance.getBusinessKey());
          assertThat(historicProcessInstance.getProcessDefinitionId()).isEqualTo(
              processInstance.getProcessDefinitionId()
          );
          assertThat(historicProcessInstance.getStartTime()).isEqualTo(noon);
          assertThat(historicProcessInstance.getEndTime()).isNull();
          assertThat(historicProcessInstance.getDurationInMillis()).isNull();
  
          List<Task> tasks = taskService.createTaskQuery().processInstanceId(processInstance.getId()).list();
  
          assertThat(tasks).hasSize(1);
  
          // in this test scenario we assume that 25 seconds after the process
          // start, the
          // user completes the task (yes! he must be almost as fast as me)
          Date twentyFiveSecsAfterNoon = new Date(noon.getTime() + 25 * 1000);
          processEngineConfiguration.getClock().setCurrentTime(twentyFiveSecsAfterNoon);
          taskService.complete(tasks.get(0).getId());
  
          historicProcessInstance = historyService
              .createHistoricProcessInstanceQuery()
              .processInstanceId(processInstance.getId())
              .singleResult();
  
          assertThat(historicProcessInstance).isNotNull();
          assertThat(historicProcessInstance.getId()).isEqualTo(processInstance.getId());
          assertThat(historicProcessInstance.getProcessDefinitionId()).isEqualTo(
              processInstance.getProcessDefinitionId()
          );
          assertThat(historicProcessInstance.getStartTime()).isEqualTo(noon);
          assertThat(historicProcessInstance.getEndTime()).isEqualTo(twentyFiveSecsAfterNoon);
          assertThat(historicProcessInstance.getDurationInMillis()).isEqualTo(Long.valueOf(25 * 1000));
  
          assertThat(historyService.createHistoricProcessInstanceQuery().unfinished().count()).isEqualTo(0);
          assertThat(historyService.createHistoricProcessInstanceQuery().finished().count()).isEqualTo(1);
      }
  
      @Deployment(resources = { "org/activiti/engine/test/history/oneTaskProcess.bpmn20.xml" })
      public void testDeleteProcessInstanceHistoryCreated() {
          ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("oneTaskProcess");
          assertThat(processInstance).isNotNull();
  
          // delete process instance should not delete the history
          runtimeService.deleteProcessInstance(processInstance.getId(), "cancel");
          HistoricProcessInstance historicProcessInstance = historyService
              .createHistoricProcessInstanceQuery()
              .processInstanceId(processInstance.getId())
              .singleResult();
          assertThat(historicProcessInstance.getEndTime()).isNotNull();
      }
  
      @Deployment(resources = { "org/activiti/engine/test/history/oneTaskProcess.bpmn20.xml" })
      public void testHistoricProcessInstanceQuery() {
          Calendar startTime = Calendar.getInstance();
  
          processEngineConfiguration.getClock().setCurrentTime(startTime.getTime());
          ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("oneTaskProcess", "businessKey123");
          runtimeService.addUserIdentityLink(processInstance.getId(), "kermit", "someType");
          runtimeService.setProcessInstanceName(processInstance.getId(), "The name");
          Calendar hourAgo = Calendar.getInstance();
          hourAgo.add(Calendar.HOUR_OF_DAY, -1);
          Calendar hourFromNow = Calendar.getInstance();
          hourFromNow.add(Calendar.HOUR_OF_DAY, 1);
  
          // Name and name like
          assertThat(
              historyService.createHistoricProcessInstanceQuery().processInstanceName("The name").singleResult().getName()
          ).isEqualTo("The name");
          assertThat(
              historyService.createHistoricProcessInstanceQuery().processInstanceName("The name").count()
          ).isEqualTo(1);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().processInstanceName("Other name").count()
          ).isEqualTo(0);
  
          assertThat(
              historyService.createHistoricProcessInstanceQuery().processInstanceNameLike("% name").count()
          ).isEqualTo(1);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().processInstanceNameLike("%nope").count()
          ).isEqualTo(0);
  
          // Query after update name
          runtimeService.setProcessInstanceName(processInstance.getId(), "New name");
          assertThat(
              historyService.createHistoricProcessInstanceQuery().processInstanceName("New name").singleResult().getName()
          ).isEqualTo("New name");
          assertThat(
              historyService.createHistoricProcessInstanceQuery().processInstanceName("New name").count()
          ).isEqualTo(1);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().processInstanceName("The name").count()
          ).isEqualTo(0);
  
          assertThat(
              historyService.createHistoricProcessInstanceQuery().processInstanceNameLike("New %").count()
          ).isEqualTo(1);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().processInstanceNameLike("The %").count()
          ).isEqualTo(0);
  
          // Start/end dates
          assertThat(
              historyService.createHistoricProcessInstanceQuery().finishedBefore(hourAgo.getTime()).count()
          ).isEqualTo(0);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().finishedBefore(hourFromNow.getTime()).count()
          ).isEqualTo(0);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().finishedAfter(hourAgo.getTime()).count()
          ).isEqualTo(0);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().finishedAfter(hourFromNow.getTime()).count()
          ).isEqualTo(0);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().startedBefore(hourFromNow.getTime()).count()
          ).isEqualTo(1);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().startedBefore(hourAgo.getTime()).count()
          ).isEqualTo(0);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().startedAfter(hourAgo.getTime()).count()
          ).isEqualTo(1);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().startedAfter(hourFromNow.getTime()).count()
          ).isEqualTo(0);
  
          // General fields
          assertThat(historyService.createHistoricProcessInstanceQuery().finished().count()).isEqualTo(0);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstance.getId()).count()
          ).isEqualTo(1);
          assertThat(
              historyService
                  .createHistoricProcessInstanceQuery()
                  .processDefinitionId(processInstance.getProcessDefinitionId())
                  .count()
          ).isEqualTo(1);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().processDefinitionKey("oneTaskProcess").count()
          ).isEqualTo(1);
          assertThat(
              historyService
                  .createHistoricProcessInstanceQuery()
                  .processDefinitionKeyIn(singletonList("oneTaskProcess"))
                  .count()
          ).isEqualTo(1);
          assertThat(
              historyService
                  .createHistoricProcessInstanceQuery()
                  .processDefinitionKeyIn(asList("undefined", "oneTaskProcess"))
                  .count()
          ).isEqualTo(1);
          assertThat(
              historyService
                  .createHistoricProcessInstanceQuery()
                  .processDefinitionKeyIn(asList("undefined1", "undefined2"))
                  .count()
          ).isEqualTo(0);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().processInstanceBusinessKey("businessKey123").count()
          ).isEqualTo(1);
  
          List<String> excludeIds = new ArrayList<String>();
          excludeIds.add("unexistingProcessDefinition");
  
          assertThat(
              historyService.createHistoricProcessInstanceQuery().processDefinitionKeyNotIn(excludeIds).count()
          ).isEqualTo(1);
  
          excludeIds.add("oneTaskProcess");
          assertThat(
              historyService.createHistoricProcessInstanceQuery().processDefinitionKeyNotIn(excludeIds).count()
          ).isEqualTo(0);
  
          // After finishing process
          taskService.complete(
              taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult().getId()
          );
          assertThat(historyService.createHistoricProcessInstanceQuery().finished().count()).isEqualTo(1);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().finishedBefore(hourAgo.getTime()).count()
          ).isEqualTo(0);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().finishedBefore(hourFromNow.getTime()).count()
          ).isEqualTo(1);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().finishedAfter(hourAgo.getTime()).count()
          ).isEqualTo(1);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().finishedAfter(hourFromNow.getTime()).count()
          ).isEqualTo(0);
  
          // Check identity links
          assertThat(historyService.createHistoricProcessInstanceQuery().involvedUser("kermit").count()).isEqualTo(1);
          assertThat(historyService.createHistoricProcessInstanceQuery().involvedUser("gonzo").count()).isEqualTo(0);
      }
  
      @Deployment(resources = { "org/activiti/engine/test/history/oneTaskProcess.bpmn20.xml" })
      public void testHistoricProcessInstanceOrQuery() {
          Calendar startTime = Calendar.getInstance();
  
          processEngineConfiguration.getClock().setCurrentTime(startTime.getTime());
          ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("oneTaskProcess", "businessKey123");
          runtimeService.addUserIdentityLink(processInstance.getId(), "kermit", "someType");
          runtimeService.setProcessInstanceName(processInstance.getId(), "The name");
          Calendar hourAgo = Calendar.getInstance();
          hourAgo.add(Calendar.HOUR_OF_DAY, -1);
          Calendar hourFromNow = Calendar.getInstance();
          hourFromNow.add(Calendar.HOUR_OF_DAY, 1);
  
          // Name and name like
          assertThat(
              historyService
                  .createHistoricProcessInstanceQuery()
                  .or()
                  .processInstanceName("The name")
                  .processDefinitionId("undefined")
                  .endOr()
                  .singleResult()
                  .getName()
          ).isEqualTo("The name");
          assertThat(
              historyService
                  .createHistoricProcessInstanceQuery()
                  .or()
                  .processInstanceName("The name")
                  .processDefinitionId("undefined")
                  .endOr()
                  .count()
          ).isEqualTo(1);
          assertThat(
              historyService
                  .createHistoricProcessInstanceQuery()
                  .or()
                  .processInstanceName("Other name")
                  .processDefinitionId("undefined")
                  .endOr()
                  .count()
          ).isEqualTo(0);
  
          assertThat(
              historyService
                  .createHistoricProcessInstanceQuery()
                  .or()
                  .processInstanceNameLike("% name")
                  .processDefinitionId("undefined")
                  .endOr()
                  .count()
          ).isEqualTo(1);
          assertThat(
              historyService
                  .createHistoricProcessInstanceQuery()
                  .or()
                  .processInstanceNameLike("%nope")
                  .processDefinitionId("undefined")
                  .endOr()
                  .count()
          ).isEqualTo(0);
  
          assertThat(
              historyService
                  .createHistoricProcessInstanceQuery()
                  .or()
                  .processInstanceName("The name")
                  .processDefinitionId("undefined")
                  .endOr()
                  .or()
                  .processInstanceNameLike("% name")
                  .processDefinitionId("undefined")
                  .endOr()
                  .count()
          ).isEqualTo(1);
  
          assertThat(
              historyService
                  .createHistoricProcessInstanceQuery()
                  .or()
                  .processInstanceName("The name")
                  .processDefinitionId("undefined")
                  .endOr()
                  .or()
                  .processInstanceNameLike("undefined")
                  .processDefinitionId("undefined")
                  .endOr()
                  .count()
          ).isEqualTo(0);
  
          // Query after update name
          runtimeService.setProcessInstanceName(processInstance.getId(), "New name");
          assertThat(
              historyService
                  .createHistoricProcessInstanceQuery()
                  .or()
                  .processInstanceName("New name")
                  .processDefinitionId("undefined")
                  .endOr()
                  .singleResult()
                  .getName()
          ).isEqualTo("New name");
          assertThat(
              historyService
                  .createHistoricProcessInstanceQuery()
                  .or()
                  .processInstanceName("New name")
                  .processDefinitionId("undefined")
                  .endOr()
                  .count()
          ).isEqualTo(1);
          assertThat(
              historyService
                  .createHistoricProcessInstanceQuery()
                  .or()
                  .processInstanceName("The name")
                  .processDefinitionId("undefined")
                  .endOr()
                  .count()
          ).isEqualTo(0);
  
          assertThat(
              historyService
                  .createHistoricProcessInstanceQuery()
                  .or()
                  .processInstanceNameLike("New %")
                  .processDefinitionId("undefined")
                  .endOr()
                  .count()
          ).isEqualTo(1);
          assertThat(
              historyService
                  .createHistoricProcessInstanceQuery()
                  .or()
                  .processInstanceNameLike("The %")
                  .processDefinitionId("undefined")
                  .endOr()
                  .count()
          ).isEqualTo(0);
  
          // Start/end dates
          assertThat(
              historyService
                  .createHistoricProcessInstanceQuery()
                  .or()
                  .finishedBefore(hourAgo.getTime())
                  .processDefinitionId("undefined")
                  .endOr()
                  .count()
          ).isEqualTo(0);
          assertThat(
              historyService
                  .createHistoricProcessInstanceQuery()
                  .or()
                  .finishedBefore(hourFromNow.getTime())
                  .processDefinitionId("undefined")
                  .endOr()
                  .count()
          ).isEqualTo(0);
          assertThat(
              historyService
                  .createHistoricProcessInstanceQuery()
                  .or()
                  .finishedAfter(hourAgo.getTime())
                  .processDefinitionId("undefined")
                  .endOr()
                  .count()
          ).isEqualTo(0);
          assertThat(
              historyService
                  .createHistoricProcessInstanceQuery()
                  .or()
                  .finishedAfter(hourFromNow.getTime())
                  .processDefinitionId("undefined")
                  .endOr()
                  .count()
          ).isEqualTo(0);
          assertThat(
              historyService
                  .createHistoricProcessInstanceQuery()
                  .or()
                  .startedBefore(hourFromNow.getTime())
                  .processDefinitionId("undefined")
                  .endOr()
                  .count()
          ).isEqualTo(1);
          assertThat(
              historyService
                  .createHistoricProcessInstanceQuery()
                  .or()
                  .startedBefore(hourAgo.getTime())
                  .processDefinitionId("undefined")
                  .endOr()
                  .count()
          ).isEqualTo(0);
          assertThat(
              historyService
                  .createHistoricProcessInstanceQuery()
                  .or()
                  .startedAfter(hourAgo.getTime())
                  .processDefinitionId("undefined")
                  .endOr()
                  .count()
          ).isEqualTo(1);
          assertThat(
              historyService
                  .createHistoricProcessInstanceQuery()
                  .or()
                  .startedAfter(hourFromNow.getTime())
                  .processDefinitionId("undefined")
                  .endOr()
                  .count()
          ).isEqualTo(0);
  
          // General fields
          assertThat(historyService.createHistoricProcessInstanceQuery().or().finished().count()).isEqualTo(0);
          assertThat(
              historyService
                  .createHistoricProcessInstanceQuery()
                  .or()
                  .processInstanceId(processInstance.getId())
                  .processDefinitionId("undefined")
                  .endOr()
                  .count()
          ).isEqualTo(1);
          assertThat(
              historyService
                  .createHistoricProcessInstanceQuery()
                  .or()
                  .processDefinitionId(processInstance.getProcessDefinitionId())
                  .processDefinitionKey("undefined")
                  .count()
          ).isEqualTo(1);
          assertThat(
              historyService
                  .createHistoricProcessInstanceQuery()
                  .or()
                  .processDefinitionId("undefined")
                  .processDefinitionKeyIn(asList("undefined", "oneTaskProcess"))
                  .endOr()
                  .count()
          ).isEqualTo(1);
          assertThat(
              historyService
                  .createHistoricProcessInstanceQuery()
                  .or()
                  .processDefinitionId("undefined")
                  .processDefinitionKeyIn(asList("undefined1", "undefined2"))
                  .endOr()
                  .count()
          ).isEqualTo(0);
          assertThat(
              historyService
                  .createHistoricProcessInstanceQuery()
                  .or()
                  .processDefinitionKey("oneTaskProcess")
                  .processDefinitionId("undefined")
                  .endOr()
                  .count()
          ).isEqualTo(1);
          assertThat(
              historyService
                  .createHistoricProcessInstanceQuery()
                  .or()
                  .processInstanceBusinessKey("businessKey123")
                  .processDefinitionId("undefined")
                  .endOr()
                  .count()
          ).isEqualTo(1);
  
          List<String> excludeIds = new ArrayList<String>();
          excludeIds.add("unexistingProcessDefinition");
  
          assertThat(
              historyService
                  .createHistoricProcessInstanceQuery()
                  .or()
                  .processDefinitionKeyNotIn(excludeIds)
                  .processDefinitionId("undefined")
                  .endOr()
                  .count()
          ).isEqualTo(1);
  
          excludeIds.add("oneTaskProcess");
          assertThat(
              historyService
                  .createHistoricProcessInstanceQuery()
                  .or()
                  .processDefinitionKeyNotIn(excludeIds)
                  .processDefinitionId("undefined")
                  .endOr()
                  .count()
          ).isEqualTo(0);
  
          // After finishing process
          taskService.complete(
              taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult().getId()
          );
          assertThat(
              historyService
                  .createHistoricProcessInstanceQuery()
                  .or()
                  .finished()
                  .processDefinitionId("undefined")
                  .endOr()
                  .count()
          ).isEqualTo(1);
          assertThat(
              historyService
                  .createHistoricProcessInstanceQuery()
                  .or()
                  .finishedBefore(hourAgo.getTime())
                  .processDefinitionId("undefined")
                  .endOr()
                  .count()
          ).isEqualTo(0);
          assertThat(
              historyService
                  .createHistoricProcessInstanceQuery()
                  .or()
                  .finishedBefore(hourFromNow.getTime())
                  .processDefinitionId("undefined")
                  .endOr()
                  .count()
          ).isEqualTo(1);
          assertThat(
              historyService
                  .createHistoricProcessInstanceQuery()
                  .or()
                  .finishedAfter(hourAgo.getTime())
                  .processDefinitionId("undefined")
                  .endOr()
                  .count()
          ).isEqualTo(1);
          assertThat(
              historyService
                  .createHistoricProcessInstanceQuery()
                  .or()
                  .finishedAfter(hourFromNow.getTime())
                  .processDefinitionId("undefined")
                  .endOr()
                  .count()
          ).isEqualTo(0);
  
          // Check identity links
          assertThat(
              historyService
                  .createHistoricProcessInstanceQuery()
                  .or()
                  .involvedUser("kermit")
                  .processDefinitionId("undefined")
                  .endOr()
                  .count()
          ).isEqualTo(1);
          assertThat(
              historyService
                  .createHistoricProcessInstanceQuery()
                  .or()
                  .involvedUser("gonzo")
                  .processDefinitionId("undefined")
                  .endOr()
                  .count()
          ).isEqualTo(0);
      }
  
      @Deployment(resources = { "org/activiti/engine/test/history/oneTaskProcess.bpmn20.xml" })
      public void testHistoricProcessInstanceSorting() {
          ProcessInstance processInstance1 = runtimeService.startProcessInstanceByKey("oneTaskProcess");
  
          assertThat(historyService.createHistoricProcessInstanceQuery().orderByProcessInstanceId().asc().list()).hasSize(
              1
          );
          assertThat(
              historyService.createHistoricProcessInstanceQuery().orderByProcessInstanceStartTime().asc().list()
          ).hasSize(1);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().orderByProcessInstanceEndTime().asc().list()
          ).hasSize(1);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().orderByProcessInstanceDuration().asc().list()
          ).hasSize(1);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().orderByProcessDefinitionId().asc().list()
          ).hasSize(1);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().orderByProcessInstanceBusinessKey().asc().list()
          ).hasSize(1);
  
          assertThat(
              historyService.createHistoricProcessInstanceQuery().orderByProcessInstanceId().desc().list()
          ).hasSize(1);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().orderByProcessInstanceStartTime().desc().list()
          ).hasSize(1);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().orderByProcessInstanceEndTime().desc().list()
          ).hasSize(1);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().orderByProcessInstanceDuration().desc().list()
          ).hasSize(1);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().orderByProcessDefinitionId().desc().list()
          ).hasSize(1);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().orderByProcessInstanceBusinessKey().desc().list()
          ).hasSize(1);
  
          assertThat(
              historyService.createHistoricProcessInstanceQuery().orderByProcessInstanceId().asc().count()
          ).isEqualTo(1);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().orderByProcessInstanceStartTime().asc().count()
          ).isEqualTo(1);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().orderByProcessInstanceEndTime().asc().count()
          ).isEqualTo(1);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().orderByProcessInstanceDuration().asc().count()
          ).isEqualTo(1);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().orderByProcessDefinitionId().asc().count()
          ).isEqualTo(1);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().orderByProcessInstanceBusinessKey().asc().count()
          ).isEqualTo(1);
  
          assertThat(
              historyService.createHistoricProcessInstanceQuery().orderByProcessInstanceId().desc().count()
          ).isEqualTo(1);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().orderByProcessInstanceStartTime().desc().count()
          ).isEqualTo(1);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().orderByProcessInstanceEndTime().desc().count()
          ).isEqualTo(1);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().orderByProcessInstanceDuration().desc().count()
          ).isEqualTo(1);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().orderByProcessDefinitionId().desc().count()
          ).isEqualTo(1);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().orderByProcessInstanceBusinessKey().desc().count()
          ).isEqualTo(1);
  
          ProcessInstance processInstance2 = runtimeService.startProcessInstanceByKey("oneTaskProcess");
  
          // First complete process instance 2
          for (Task task : taskService.createTaskQuery().processInstanceId(processInstance2.getId()).list()) {
              taskService.complete(task.getId());
          }
  
          // Then process instance 1
          for (Task task : taskService.createTaskQuery().processInstanceId(processInstance1.getId()).list()) {
              taskService.complete(task.getId());
          }
  
          assertThat(historyService.createHistoricProcessInstanceQuery().orderByProcessInstanceId().asc().list()).hasSize(
              2
          );
          assertThat(
              historyService.createHistoricProcessInstanceQuery().orderByProcessInstanceStartTime().asc().list()
          ).hasSize(2);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().orderByProcessInstanceEndTime().asc().list()
          ).hasSize(2);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().orderByProcessInstanceDuration().asc().list()
          ).hasSize(2);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().orderByProcessDefinitionId().asc().list()
          ).hasSize(2);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().orderByProcessInstanceBusinessKey().asc().list()
          ).hasSize(2);
  
          assertThat(
              historyService.createHistoricProcessInstanceQuery().orderByProcessInstanceId().desc().list()
          ).hasSize(2);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().orderByProcessInstanceStartTime().desc().list()
          ).hasSize(2);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().orderByProcessInstanceEndTime().desc().list()
          ).hasSize(2);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().orderByProcessInstanceDuration().desc().list()
          ).hasSize(2);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().orderByProcessDefinitionId().desc().list()
          ).hasSize(2);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().orderByProcessInstanceBusinessKey().desc().list()
          ).hasSize(2);
  
          assertThat(
              historyService.createHistoricProcessInstanceQuery().orderByProcessInstanceId().asc().count()
          ).isEqualTo(2);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().orderByProcessInstanceStartTime().asc().count()
          ).isEqualTo(2);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().orderByProcessInstanceEndTime().asc().count()
          ).isEqualTo(2);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().orderByProcessInstanceDuration().asc().count()
          ).isEqualTo(2);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().orderByProcessDefinitionId().asc().count()
          ).isEqualTo(2);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().orderByProcessInstanceBusinessKey().asc().count()
          ).isEqualTo(2);
  
          assertThat(
              historyService.createHistoricProcessInstanceQuery().orderByProcessInstanceId().desc().count()
          ).isEqualTo(2);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().orderByProcessInstanceStartTime().desc().count()
          ).isEqualTo(2);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().orderByProcessInstanceEndTime().desc().count()
          ).isEqualTo(2);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().orderByProcessInstanceDuration().desc().count()
          ).isEqualTo(2);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().orderByProcessDefinitionId().desc().count()
          ).isEqualTo(2);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().orderByProcessInstanceBusinessKey().desc().count()
          ).isEqualTo(2);
  
          // Verify orderByProcessInstanceEndTime
          List<HistoricProcessInstance> historicProcessInstances = historyService
              .createHistoricProcessInstanceQuery()
              .orderByProcessInstanceEndTime()
              .desc()
              .list();
          List<String> processInstanceIds = new ArrayList<String>(2);
          processInstanceIds.add(historicProcessInstances.get(0).getId());
          processInstanceIds.add(historicProcessInstances.get(1).getId());
          assertThat(processInstanceIds.contains(processInstance1.getId())).isTrue();
          assertThat(processInstanceIds.contains(processInstance2.getId())).isTrue();
  
          // Verify again, with variables included (bug reported on that)
          historicProcessInstances = historyService
              .createHistoricProcessInstanceQuery()
              .orderByProcessInstanceEndTime()
              .desc()
              .includeProcessVariables()
              .list();
          processInstanceIds = new ArrayList<String>(2);
          processInstanceIds.add(historicProcessInstances.get(0).getId());
          processInstanceIds.add(historicProcessInstances.get(1).getId());
          assertThat(processInstanceIds.contains(processInstance1.getId())).isTrue();
          assertThat(processInstanceIds.contains(processInstance2.getId())).isTrue();
      }
  
      public void testInvalidSorting() {
          assertThatExceptionOfType(ActivitiIllegalArgumentException.class).isThrownBy(() ->
              historyService.createHistoricProcessInstanceQuery().asc()
          );
  
          assertThatExceptionOfType(ActivitiIllegalArgumentException.class).isThrownBy(() ->
              historyService.createHistoricProcessInstanceQuery().desc()
          );
  
          assertThatExceptionOfType(ActivitiIllegalArgumentException.class).isThrownBy(() ->
              historyService.createHistoricProcessInstanceQuery().orderByProcessInstanceId().list()
          );
      }
  
      @Deployment(resources = { "org/activiti/engine/test/history/oneTaskProcess.bpmn20.xml" })
      // ACT-1098
      public void testDeleteReason() {
          if (processEngineConfiguration.getHistoryLevel().isAtLeast(HistoryLevel.ACTIVITY)) {
              final String deleteReason = "some delete reason";
              ProcessInstance pi = runtimeService.startProcessInstanceByKey("oneTaskProcess");
              runtimeService.deleteProcessInstance(pi.getId(), deleteReason);
              HistoricProcessInstance hpi = historyService
                  .createHistoricProcessInstanceQuery()
                  .processInstanceId(pi.getId())
                  .singleResult();
              assertThat(hpi.getDeleteReason()).isEqualTo(deleteReason);
          }
      }
  
      @Deployment(resources = { "org/activiti/engine/test/history/oneTaskProcess.bpmn20.xml" })
      public void testHistoricIdentityLinksOnProcessInstance() {
          if (processEngineConfiguration.getHistoryLevel().isAtLeast(HistoryLevel.AUDIT)) {
              ProcessInstance pi = runtimeService.startProcessInstanceByKey("oneTaskProcess");
              runtimeService.addUserIdentityLink(pi.getId(), "kermit", "myType");
  
              // Check historic links
              List<HistoricIdentityLink> historicLinks = historyService.getHistoricIdentityLinksForProcessInstance(
                  pi.getId()
              );
              assertThat(historicLinks).hasSize(1);
  
              assertThat(historicLinks.get(0).getType()).isEqualTo("myType");
              assertThat(historicLinks.get(0).getUserId()).isEqualTo("kermit");
              assertThat(historicLinks.get(0).getGroupId()).isNull();
              assertThat(historicLinks.get(0).getProcessInstanceId()).isEqualTo(pi.getId());
  
              // When process is ended, link should remain
              taskService.complete(taskService.createTaskQuery().processInstanceId(pi.getId()).singleResult().getId());
              assertThat(
                  runtimeService.createProcessInstanceQuery().processInstanceId(pi.getId()).singleResult()
              ).isNull();
  
              assertThat(historyService.getHistoricIdentityLinksForProcessInstance(pi.getId())).hasSize(1);
  
              // When process is deleted, identitylinks shouldn't exist anymore
              historyService.deleteHistoricProcessInstance(pi.getId());
              assertThat(historyService.getHistoricIdentityLinksForProcessInstance(pi.getId())).hasSize(0);
          }
      }
  
      @Deployment(resources = { "org/activiti/engine/test/history/oneTaskProcess.bpmn20.xml" })
      public void testHistoricIdentityLinksOnProcessInstanceWithDetails() {
          if (processEngineConfiguration.getHistoryLevel().isAtLeast(HistoryLevel.AUDIT)) {
              ProcessInstance pi = runtimeService.startProcessInstanceByKey("oneTaskProcess");
              runtimeService.addUserIdentityLink(pi.getId(), "kermit", "myType", "details".getBytes());
  
              // Check historic links
              List<HistoricIdentityLink> historicLinks = historyService.getHistoricIdentityLinksForProcessInstance(
                  pi.getId()
              );
              assertThat(historicLinks).hasSize(1);
  
              assertThat(historicLinks.get(0).getType()).isEqualTo("myType");
              assertThat(historicLinks.get(0).getUserId()).isEqualTo("kermit");
              assertThat(historicLinks.get(0).getGroupId()).isNull();
              assertThat(historicLinks.get(0).getProcessInstanceId()).isEqualTo(pi.getId());
              assertThat(historicLinks.get(0).getDetails()).isEqualTo("details".getBytes());
  
              // When process is ended, link should remain
              taskService.complete(taskService.createTaskQuery().processInstanceId(pi.getId()).singleResult().getId());
              assertThat(
                  runtimeService.createProcessInstanceQuery().processInstanceId(pi.getId()).singleResult()
              ).isNull();
  
              assertThat(historyService.getHistoricIdentityLinksForProcessInstance(pi.getId())).hasSize(1);
  
              // When process is deleted, identitylinks shouldn't exist anymore
              historyService.deleteHistoricProcessInstance(pi.getId());
              assertThat(historyService.getHistoricIdentityLinksForProcessInstance(pi.getId())).hasSize(0);
          }
      }
  
      /**
       * Validation for ACT-821
       */
      @Deployment(
          resources = {
              "org/activiti/engine/test/history/HistoricProcessInstanceTest.testDeleteHistoricProcessInstanceWithCallActivity.bpmn20.xml",
              "org/activiti/engine/test/history/HistoricProcessInstanceTest.testDeleteHistoricProcessInstanceWithCallActivity-subprocess.bpmn20.xml",
          }
      )
      public void testDeleteHistoricProcessInstanceWithCallActivity() {
          if (processEngineConfiguration.getHistoryLevel().isAtLeast(HistoryLevel.AUDIT)) {
              ProcessInstance pi = runtimeService.startProcessInstanceByKey("callSimpleSubProcess");
  
              runtimeService.deleteProcessInstance(pi.getId(), "testing");
  
              // The parent and child process should be present in history
              assertThat(historyService.createHistoricProcessInstanceQuery().count()).isEqualTo(2L);
  
              // Deleting the parent process should cascade the child-process
              historyService.deleteHistoricProcessInstance(pi.getId());
              assertThat(historyService.createHistoricProcessInstanceQuery().count()).isEqualTo(0L);
          }
      }
  
      @Deployment(resources = { "org/activiti/engine/test/history/oneTaskProcess.bpmn20.xml" })
      public void testHistoricProcessInstanceName() {
          String piName = "Customized Process Instance Name";
          ProcessInstanceBuilder builder = runtimeService.createProcessInstanceBuilder();
          builder.processDefinitionKey("oneTaskProcess");
          builder.name(piName);
          ProcessInstance processInstance1 = builder.start();
  
          HistoricProcessInstance historicProcessInstance = historyService
              .createHistoricProcessInstanceQuery()
              .processInstanceId(processInstance1.getProcessInstanceId())
              .singleResult();
          assertThat(historicProcessInstance.getName()).isEqualTo(piName);
          assertThat(historyService.createHistoricProcessInstanceQuery().processInstanceName(piName).list()).hasSize(1);
      }
  
      public void testNameAndTenantIdSetWhenFetchingVariables() {
          String tenantId = "testTenantId";
          String processInstanceName = "myProcessInstance";
  
          String deploymentId = repositoryService
              .createDeployment()
              .addClasspathResource("org/activiti/engine/test/history/oneTaskProcess.bpmn20.xml")
              .tenantId(tenantId)
              .deploy()
              .getId();
  
          Map<String, Object> vars = new HashMap<String, Object>();
          vars.put("name", "Kermit");
          vars.put("age", 60);
          ProcessInstance processInstance = runtimeService.startProcessInstanceByKeyAndTenantId(
              "oneTaskProcess",
              vars,
              tenantId
          );
          runtimeService.setProcessInstanceName(processInstance.getId(), processInstanceName);
  
          // Verify name and tenant id (didn't work on mssql and db2) on process instance
          List<ProcessInstance> processInstances = runtimeService
              .createProcessInstanceQuery()
              .includeProcessVariables()
              .list();
          assertThat(processInstances).hasSize(1);
          processInstance = processInstances.get(0);
  
          assertThat(processInstance.getName()).isEqualTo(processInstanceName);
          assertThat(processInstance.getTenantId()).isEqualTo(tenantId);
  
          Map<String, Object> processInstanceVars = processInstance.getProcessVariables();
          assertThat(processInstanceVars).hasSize(2);
          assertThat(processInstanceVars.get("name")).isEqualTo("Kermit");
          assertThat(processInstanceVars.get("age")).isEqualTo(60);
  
          // Verify name and tenant id (didn't work on mssql and db2) on historic process instance
          List<HistoricProcessInstance> historicProcessInstances = historyService
              .createHistoricProcessInstanceQuery()
              .includeProcessVariables()
              .list();
          assertThat(historicProcessInstances).hasSize(1);
          HistoricProcessInstance historicProcessInstance = historicProcessInstances.get(0);
  
          // Verify name and tenant id (didn't work on mssql and db2) on process instance
          assertThat(historicProcessInstance.getName()).isEqualTo(processInstanceName);
          assertThat(historicProcessInstance.getTenantId()).isEqualTo(tenantId);
  
          Map<String, Object> historicProcessInstanceVars = historicProcessInstance.getProcessVariables();
          assertThat(historicProcessInstanceVars).hasSize(2);
          assertThat(historicProcessInstanceVars.get("name")).isEqualTo("Kermit");
          assertThat(historicProcessInstanceVars.get("age")).isEqualTo(60);
  
          // cleanup
          repositoryService.deleteDeployment(deploymentId, true);
      }
  }
  