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.api.tenant;
  
  import static java.util.Collections.singletonMap;
  import static org.assertj.core.api.Assertions.assertThat;
  import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
  
  import java.util.ArrayList;
  import java.util.List;
  import org.activiti.engine.ActivitiException;
  import org.activiti.engine.ProcessEngineConfiguration;
  import org.activiti.engine.history.HistoricActivityInstance;
  import org.activiti.engine.impl.history.HistoryLevel;
  import org.activiti.engine.impl.test.PluggableActivitiTestCase;
  import org.activiti.engine.repository.Deployment;
  import org.activiti.engine.repository.Model;
  import org.activiti.engine.repository.ProcessDefinition;
  import org.activiti.engine.runtime.Job;
  import org.activiti.engine.runtime.ProcessInstance;
  import org.activiti.engine.task.Task;
  
  /**
   * A test case for the various implications of the tenancy support (tenant id column to entities + query support)
   */
  public class TenancyTest extends PluggableActivitiTestCase {
  
      private static final String TEST_TENANT_ID = "myTenantId";
  
      private List<String> autoCleanedUpDeploymentIds = new ArrayList<String>();
  
      @Override
      protected void setUp() throws Exception {
          super.setUp();
          this.autoCleanedUpDeploymentIds.clear();
      }
  
      @Override
      protected void tearDown() throws Exception {
          super.tearDown();
  
          if (!autoCleanedUpDeploymentIds.isEmpty()) {
              for (String deploymentId : autoCleanedUpDeploymentIds) {
                  repositoryService.deleteDeployment(deploymentId, true);
              }
          }
      }
  
      /**
       * Deploys the one task process with the test tenant id.
       * @return The process definition id of the deployed process definition.
       */
      private String deployTestProcessWithTestTenant() {
          return deployTestProcessWithTestTenant(TEST_TENANT_ID);
      }
  
      private String deployTestProcessWithTestTenant(String tenantId) {
          String id = repositoryService
              .createDeployment()
              .addBpmnModel("testProcess.bpmn20.xml", createOneTaskTestProcess())
              .tenantId(tenantId)
              .deploy()
              .getId();
  
          autoCleanedUpDeploymentIds.add(id);
  
          return repositoryService.createProcessDefinitionQuery().deploymentId(id).singleResult().getId();
      }
  
      private String deployTestProcessWithTwoTasksWithTestTenant() {
          String id = repositoryService
              .createDeployment()
              .addBpmnModel("testProcess.bpmn20.xml", createTwoTasksTestProcess())
              .tenantId(TEST_TENANT_ID)
              .deploy()
              .getId();
  
          autoCleanedUpDeploymentIds.add(id);
  
          return repositoryService.createProcessDefinitionQuery().deploymentId(id).singleResult().getId();
      }
  
      private String deployTestProcessWithTwoTasksNoTenant() {
          String id = repositoryService
              .createDeployment()
              .addBpmnModel("testProcess.bpmn20.xml", createTwoTasksTestProcess())
              .deploy()
              .getId();
  
          autoCleanedUpDeploymentIds.add(id);
  
          return repositoryService.createProcessDefinitionQuery().deploymentId(id).singleResult().getId();
      }
  
      public void testDeploymentTenancy() {
          deployTestProcessWithTestTenant();
  
          assertThat(repositoryService.createDeploymentQuery().singleResult().getTenantId()).isEqualTo(TEST_TENANT_ID);
          assertThat(repositoryService.createDeploymentQuery().deploymentTenantId(TEST_TENANT_ID).list()).hasSize(1);
          assertThat(
              repositoryService
                  .createDeploymentQuery()
                  .deploymentId(autoCleanedUpDeploymentIds.get(0))
                  .deploymentTenantId(TEST_TENANT_ID)
                  .list()
          ).hasSize(1);
          assertThat(repositoryService.createDeploymentQuery().deploymentTenantIdLike("my%").list()).hasSize(1);
          assertThat(repositoryService.createDeploymentQuery().deploymentTenantIdLike("%TenantId").list()).hasSize(1);
          assertThat(repositoryService.createDeploymentQuery().deploymentTenantIdLike("m%Ten%").list()).hasSize(1);
          assertThat(repositoryService.createDeploymentQuery().deploymentTenantIdLike("noexisting%").list()).hasSize(0);
          assertThat(repositoryService.createDeploymentQuery().deploymentWithoutTenantId().list()).hasSize(0);
      }
  
      public void testProcessDefinitionTenancy() {
          // Deploy a process with tenant and verify
  
          String processDefinitionIdWithTenant = deployTestProcessWithTestTenant();
          assertThat(
              repositoryService
                  .createProcessDefinitionQuery()
                  .processDefinitionId(processDefinitionIdWithTenant)
                  .singleResult()
                  .getTenantId()
          ).isEqualTo(TEST_TENANT_ID);
          assertThat(
              repositoryService.createProcessDefinitionQuery().processDefinitionTenantId(TEST_TENANT_ID).list()
          ).hasSize(1);
          assertThat(repositoryService.createProcessDefinitionQuery().processDefinitionTenantIdLike("m%").list()).hasSize(
              1
          );
          assertThat(
              repositoryService.createProcessDefinitionQuery().processDefinitionTenantIdLike("somethingElse%").list()
          ).hasSize(0);
          assertThat(repositoryService.createProcessDefinitionQuery().processDefinitionWithoutTenantId().list()).hasSize(
              0
          );
  
          // Deploy another process, without tenant
          String processDefinitionIdWithoutTenant = deployOneTaskTestProcess();
          assertThat(repositoryService.createProcessDefinitionQuery().list()).hasSize(2);
          assertThat(
              repositoryService.createProcessDefinitionQuery().processDefinitionTenantId(TEST_TENANT_ID).list()
          ).hasSize(1);
          assertThat(repositoryService.createProcessDefinitionQuery().processDefinitionTenantIdLike("m%").list()).hasSize(
              1
          );
          assertThat(
              repositoryService.createProcessDefinitionQuery().processDefinitionTenantIdLike("somethingElse%").list()
          ).hasSize(0);
          assertThat(repositoryService.createProcessDefinitionQuery().processDefinitionWithoutTenantId().list()).hasSize(
              1
          );
  
          // Deploy another process with the same tenant
          String processDefinitionIdWithTenant2 = deployTestProcessWithTestTenant();
          assertThat(repositoryService.createProcessDefinitionQuery().list()).hasSize(3);
          assertThat(
              repositoryService.createProcessDefinitionQuery().processDefinitionTenantId(TEST_TENANT_ID).list()
          ).hasSize(2);
          assertThat(repositoryService.createProcessDefinitionQuery().processDefinitionTenantIdLike("m%").list()).hasSize(
              2
          );
          assertThat(
              repositoryService.createProcessDefinitionQuery().processDefinitionTenantIdLike("somethingElse%").list()
          ).hasSize(0);
          assertThat(repositoryService.createProcessDefinitionQuery().processDefinitionWithoutTenantId().list()).hasSize(
              1
          );
  
          // Extra check: we deployed the one task process twice, but once with
          // tenant and once without. The latest query should show this.
          assertThat(
              repositoryService
                  .createProcessDefinitionQuery()
                  .processDefinitionKey("oneTaskProcess")
                  .processDefinitionTenantId(TEST_TENANT_ID)
                  .latestVersion()
                  .singleResult()
                  .getId()
          ).isEqualTo(processDefinitionIdWithTenant2);
          assertThat(
              repositoryService
                  .createProcessDefinitionQuery()
                  .processDefinitionKey("oneTaskProcess")
                  .processDefinitionTenantId("Not a tenant")
                  .latestVersion()
                  .count()
          ).isEqualTo(0);
          assertThat(
              repositoryService
                  .createProcessDefinitionQuery()
                  .processDefinitionKey("oneTaskProcess")
                  .processDefinitionWithoutTenantId()
                  .latestVersion()
                  .singleResult()
                  .getId()
          ).isEqualTo(processDefinitionIdWithoutTenant);
      }
  
      public void testProcessInstanceTenancy() {
          // Start a number of process instances with tenant
          String processDefinitionId = deployTestProcessWithTestTenant();
          int nrOfProcessInstancesWithTenant = 6;
          for (int i = 0; i < nrOfProcessInstancesWithTenant; i++) {
              runtimeService.startProcessInstanceById(processDefinitionId);
          }
  
          // Start a number of process instance without tenantit
          String processDefinitionIdNoTenant = deployOneTaskTestProcess();
          int nrOfProcessInstancesNoTenant = 8;
          for (int i = 0; i < nrOfProcessInstancesNoTenant; i++) {
              runtimeService.startProcessInstanceById(processDefinitionIdNoTenant);
          }
  
          // Check the query results
          assertThat(
              runtimeService
                  .createProcessInstanceQuery()
                  .processDefinitionId(processDefinitionId)
                  .list()
                  .get(0)
                  .getTenantId()
          ).isEqualTo(TEST_TENANT_ID);
          assertThat(runtimeService.createProcessInstanceQuery().list().size()).isEqualTo(
              nrOfProcessInstancesNoTenant + nrOfProcessInstancesWithTenant
          );
          assertThat(
              runtimeService.createProcessInstanceQuery().processInstanceWithoutTenantId().list().size()
          ).isEqualTo(nrOfProcessInstancesNoTenant);
          assertThat(
              runtimeService.createProcessInstanceQuery().processInstanceTenantId(TEST_TENANT_ID).list().size()
          ).isEqualTo(nrOfProcessInstancesWithTenant);
          assertThat(
              runtimeService.createProcessInstanceQuery().processInstanceTenantIdLike("%enan%").list().size()
          ).isEqualTo(nrOfProcessInstancesWithTenant);
      }
  
      public void testExecutionTenancy() {
          // Start a number of process instances with tenant
          String processDefinitionId = deployTestProcessWithTwoTasksWithTestTenant();
          int nrOfProcessInstancesWithTenant = 4;
          for (int i = 0; i < nrOfProcessInstancesWithTenant; i++) {
              runtimeService.startProcessInstanceById(processDefinitionId);
          }
  
          // Start a number of process instance without tenantid
          String processDefinitionIdNoTenant = deployTwoTasksTestProcess();
          int nrOfProcessInstancesNoTenant = 2;
          for (int i = 0; i < nrOfProcessInstancesNoTenant; i++) {
              runtimeService.startProcessInstanceById(processDefinitionIdNoTenant);
          }
  
          // Check the query results:
          // note: 3 executions per process instance due to parallelism!
          assertThat(
              runtimeService.createExecutionQuery().processDefinitionId(processDefinitionId).list().get(0).getTenantId()
          ).isEqualTo(TEST_TENANT_ID);
          assertThat(
              runtimeService
                  .createExecutionQuery()
                  .processDefinitionId(processDefinitionIdNoTenant)
                  .list()
                  .get(0)
                  .getTenantId()
          ).isEqualTo("");
          assertThat(runtimeService.createExecutionQuery().list().size()).isEqualTo(
              3 * (nrOfProcessInstancesNoTenant + nrOfProcessInstancesWithTenant)
          );
          assertThat(runtimeService.createExecutionQuery().executionWithoutTenantId().list().size()).isEqualTo(
              3 * nrOfProcessInstancesNoTenant
          );
          assertThat(runtimeService.createExecutionQuery().executionTenantId(TEST_TENANT_ID).list().size()).isEqualTo(
              3 * nrOfProcessInstancesWithTenant
          );
          assertThat(runtimeService.createExecutionQuery().executionTenantIdLike("%en%").list().size()).isEqualTo(
              3 * nrOfProcessInstancesWithTenant
          );
  
          // Check the process instance query results, just to be sure
          assertThat(
              runtimeService
                  .createProcessInstanceQuery()
                  .processDefinitionId(processDefinitionId)
                  .list()
                  .get(0)
                  .getTenantId()
          ).isEqualTo(TEST_TENANT_ID);
          assertThat(runtimeService.createProcessInstanceQuery().list().size()).isEqualTo(
              nrOfProcessInstancesNoTenant + nrOfProcessInstancesWithTenant
          );
          assertThat(
              runtimeService.createProcessInstanceQuery().processInstanceWithoutTenantId().list().size()
          ).isEqualTo(nrOfProcessInstancesNoTenant);
          assertThat(
              runtimeService.createProcessInstanceQuery().processInstanceTenantId(TEST_TENANT_ID).list().size()
          ).isEqualTo(nrOfProcessInstancesWithTenant);
          assertThat(
              runtimeService.createProcessInstanceQuery().processInstanceTenantIdLike("%en%").list().size()
          ).isEqualTo(nrOfProcessInstancesWithTenant);
      }
  
      public void testTaskTenancy() {
          // Generate 10 tasks with tenant
          String processDefinitionIdWithTenant = deployTestProcessWithTwoTasksWithTestTenant();
          int nrOfProcessInstancesWithTenant = 5;
          for (int i = 0; i < nrOfProcessInstancesWithTenant; i++) {
              runtimeService.startProcessInstanceById(processDefinitionIdWithTenant);
          }
  
          // Generate 4 tasks without tenant
          String processDefinitionIdNoTenant = deployTwoTasksTestProcess();
          int nrOfProcessInstancesNoTenant = 2;
          for (int i = 0; i < nrOfProcessInstancesNoTenant; i++) {
              runtimeService.startProcessInstanceById(processDefinitionIdNoTenant);
          }
  
          // Check the query results
          assertThat(
              taskService.createTaskQuery().processDefinitionId(processDefinitionIdWithTenant).list().get(0).getTenantId()
          ).isEqualTo(TEST_TENANT_ID);
          assertThat(
              taskService.createTaskQuery().processDefinitionId(processDefinitionIdNoTenant).list().get(0).getTenantId()
          ).isEqualTo("");
  
          assertThat(taskService.createTaskQuery().list().size()).isEqualTo(14);
          assertThat(taskService.createTaskQuery().taskTenantId(TEST_TENANT_ID).list().size()).isEqualTo(10);
          assertThat(taskService.createTaskQuery().taskTenantId("Another").list().size()).isEqualTo(0);
          assertThat(taskService.createTaskQuery().taskTenantIdLike("my%").list().size()).isEqualTo(10);
          assertThat(taskService.createTaskQuery().taskWithoutTenantId().list().size()).isEqualTo(4);
      }
  
      public void testJobTenancy() {
          // Deploy process with a timer and an async step AND with a tenant
          String deploymentId = repositoryService
              .createDeployment()
              .addClasspathResource("org/activiti/engine/test/api/tenant/TenancyTest.testJobTenancy.bpmn20.xml")
              .tenantId(TEST_TENANT_ID)
              .deploy()
              .getId();
  
          // verify job (timer start)
          Job job = managementService.createTimerJobQuery().singleResult();
          assertThat(job.getTenantId()).isEqualTo(TEST_TENANT_ID);
          managementService.moveTimerToExecutableJob(job.getId());
          managementService.executeJob(job.getId());
  
          // Verify Job tenancy (process intermediary timer)
          job = managementService.createTimerJobQuery().singleResult();
          assertThat(job.getTenantId()).isEqualTo(TEST_TENANT_ID);
  
          // Start process, and verify async job has correct tenant id
          managementService.moveTimerToExecutableJob(job.getId());
          managementService.executeJob(job.getId());
          job = managementService.createJobQuery().singleResult();
          assertThat(job.getTenantId()).isEqualTo(TEST_TENANT_ID);
  
          // Finish process
          managementService.executeJob(job.getId());
  
          // Do the same, but now without a tenant
          String deploymentId2 = repositoryService
              .createDeployment()
              .addClasspathResource("org/activiti/engine/test/api/tenant/TenancyTest.testJobTenancy.bpmn20.xml")
              .deploy()
              .getId();
  
          job = managementService.createTimerJobQuery().singleResult();
          assertThat(job.getTenantId()).isEqualTo("");
          managementService.moveTimerToExecutableJob(job.getId());
          managementService.executeJob(job.getId());
          job = managementService.createTimerJobQuery().singleResult();
          assertThat(job.getTenantId()).isEqualTo("");
          managementService.moveTimerToExecutableJob(job.getId());
          managementService.executeJob(job.getId());
          job = managementService.createJobQuery().singleResult();
          assertThat(job.getTenantId()).isEqualTo("");
  
          // clean up
          repositoryService.deleteDeployment(deploymentId, true);
          repositoryService.deleteDeployment(deploymentId2, true);
      }
  
      public void testModelTenancy() {
          // Create a few models with tenant
          int nrOfModelsWithTenant = 3;
          for (int i = 0; i < nrOfModelsWithTenant; i++) {
              Model model = repositoryService.newModel();
              model.setName(i + "");
              model.setTenantId(TEST_TENANT_ID);
              repositoryService.saveModel(model);
          }
  
          // Create a few models without tenant
          int nrOfModelsWithoutTenant = 5;
          for (int i = 0; i < nrOfModelsWithoutTenant; i++) {
              Model model = repositoryService.newModel();
              model.setName(i + "");
              repositoryService.saveModel(model);
          }
  
          // Check query
          assertThat(repositoryService.createModelQuery().list().size()).isEqualTo(
              nrOfModelsWithoutTenant + nrOfModelsWithTenant
          );
          assertThat(repositoryService.createModelQuery().modelWithoutTenantId().list().size()).isEqualTo(
              nrOfModelsWithoutTenant
          );
          assertThat(repositoryService.createModelQuery().modelTenantId(TEST_TENANT_ID).list().size()).isEqualTo(
              nrOfModelsWithTenant
          );
          assertThat(repositoryService.createModelQuery().modelTenantIdLike("my%").list().size()).isEqualTo(
              nrOfModelsWithTenant
          );
          assertThat(repositoryService.createModelQuery().modelTenantId("a%").list().size()).isEqualTo(0);
  
          // Clean up
          for (Model model : repositoryService.createModelQuery().list()) {
              repositoryService.deleteModel(model.getId());
          }
      }
  
      public void testChangeDeploymentTenantId() {
          // Generate 8 tasks with tenant
          String processDefinitionIdWithTenant = deployTestProcessWithTwoTasksWithTestTenant();
          int nrOfProcessInstancesWithTenant = 4;
          for (int i = 0; i < nrOfProcessInstancesWithTenant; i++) {
              runtimeService.startProcessInstanceById(processDefinitionIdWithTenant);
          }
  
          // Generate 10 tasks without tenant
          String processDefinitionIdNoTenant = deployTwoTasksTestProcess();
          int nrOfProcessInstancesNoTenant = 5;
          for (int i = 0; i < nrOfProcessInstancesNoTenant; i++) {
              runtimeService.startProcessInstanceById(processDefinitionIdNoTenant);
          }
  
          // Migrate deployment with tenant to another tenant
          String newTenantId = "NEW TENANT ID";
  
          String deploymentId = repositoryService
              .createProcessDefinitionQuery()
              .processDefinitionId(processDefinitionIdWithTenant)
              .singleResult()
              .getDeploymentId();
          repositoryService.changeDeploymentTenantId(deploymentId, newTenantId);
  
          // Verify tenant id
          Deployment deployment = repositoryService.createDeploymentQuery().deploymentId(deploymentId).singleResult();
          assertThat(deployment.getTenantId()).isEqualTo(newTenantId);
  
          // Verify deployment
          assertThat(repositoryService.createDeploymentQuery().list()).hasSize(2);
          assertThat(repositoryService.createDeploymentQuery().deploymentTenantId(TEST_TENANT_ID).list()).hasSize(0);
          assertThat(repositoryService.createDeploymentQuery().deploymentTenantId(newTenantId).list()).hasSize(1);
          assertThat(repositoryService.createDeploymentQuery().deploymentWithoutTenantId().list()).hasSize(1);
  
          // Verify process definition
          assertThat(repositoryService.createProcessDefinitionQuery().list()).hasSize(2);
          assertThat(
              repositoryService.createProcessDefinitionQuery().processDefinitionTenantId(TEST_TENANT_ID).list()
          ).hasSize(0);
          assertThat(
              repositoryService.createProcessDefinitionQuery().processDefinitionTenantId(newTenantId).list()
          ).hasSize(1);
          assertThat(
              repositoryService.createProcessDefinitionQuery().processDefinitionTenantId(newTenantId).list()
          ).hasSize(1);
  
          // Verify process instances
          assertThat(runtimeService.createProcessInstanceQuery().list()).hasSize(
              nrOfProcessInstancesNoTenant + nrOfProcessInstancesWithTenant
          );
          assertThat(runtimeService.createProcessInstanceQuery().processInstanceTenantId(TEST_TENANT_ID).list()).hasSize(
              0
          );
          assertThat(runtimeService.createProcessInstanceQuery().processInstanceTenantId(newTenantId).list()).hasSize(
              nrOfProcessInstancesWithTenant
          );
          assertThat(runtimeService.createProcessInstanceQuery().processInstanceWithoutTenantId().list()).hasSize(
              nrOfProcessInstancesNoTenant
          );
  
          // Verify executions
          assertThat(runtimeService.createExecutionQuery().list()).hasSize(
              3 * (nrOfProcessInstancesNoTenant + nrOfProcessInstancesWithTenant)
          );
          assertThat(runtimeService.createExecutionQuery().executionWithoutTenantId().list()).hasSize(
              3 * nrOfProcessInstancesNoTenant
          );
          assertThat(runtimeService.createExecutionQuery().executionTenantId(TEST_TENANT_ID).list()).hasSize(0);
          assertThat(runtimeService.createExecutionQuery().executionTenantId(newTenantId).list()).hasSize(
              3 * nrOfProcessInstancesWithTenant
          );
          assertThat(runtimeService.createExecutionQuery().executionTenantIdLike("NEW%").list()).hasSize(
              3 * nrOfProcessInstancesWithTenant
          );
  
          // Verify tasks
          assertThat(taskService.createTaskQuery().list()).hasSize(
              2 * (nrOfProcessInstancesNoTenant + nrOfProcessInstancesWithTenant)
          );
          assertThat(taskService.createTaskQuery().taskTenantId(TEST_TENANT_ID).list()).hasSize(0);
          assertThat(taskService.createTaskQuery().taskTenantId(newTenantId).list()).hasSize(
              2 * nrOfProcessInstancesWithTenant
          );
          assertThat(taskService.createTaskQuery().taskWithoutTenantId().list()).hasSize(
              2 * nrOfProcessInstancesNoTenant
          );
  
          // Remove the tenant id and verify results
          // should clash: there is already a process definition with the same key
          assertThatExceptionOfType(Exception.class).isThrownBy(() ->
              repositoryService.changeDeploymentTenantId(deploymentId, "")
          );
      }
  
      public void testChangeDeploymentIdWithClash() {
          String processDefinitionIdWithTenant = deployTestProcessWithTestTenant("tenantA");
          deployOneTaskTestProcess();
  
          // Changing the one with tenant now back to one without should clash,
          // cause there already exists one
          assertThatExceptionOfType(Exception.class).isThrownBy(() ->
              repositoryService.changeDeploymentTenantId(processDefinitionIdWithTenant, "")
          );
  
          // Deploying another version should just up the version
          String processDefinitionIdNoTenant2 = deployOneTaskTestProcess();
          assertThat(
              repositoryService
                  .createProcessDefinitionQuery()
                  .processDefinitionId(processDefinitionIdNoTenant2)
                  .singleResult()
                  .getVersion()
          ).isEqualTo(2);
      }
  
      public void testJobTenancyAfterTenantChange() {
          // Deploy process with a timer and an async step AND with a tenant
          String deploymentId = repositoryService
              .createDeployment()
              .addClasspathResource("org/activiti/engine/test/api/tenant/TenancyTest.testJobTenancy.bpmn20.xml")
              .tenantId(TEST_TENANT_ID)
              .deploy()
              .getId();
  
          String newTenant = "newTenant";
          repositoryService.changeDeploymentTenantId(deploymentId, newTenant);
  
          // verify job (timer start)
          Job job = managementService.createTimerJobQuery().singleResult();
          assertThat(job.getTenantId()).isEqualTo(newTenant);
          managementService.moveTimerToExecutableJob(job.getId());
          managementService.executeJob(job.getId());
  
          // Verify Job tenancy (process intermediary timer)
          job = managementService.createTimerJobQuery().singleResult();
          assertThat(job.getTenantId()).isEqualTo(newTenant);
  
          // Start process, and verify async job has correct tenant id
          managementService.moveTimerToExecutableJob(job.getId());
          managementService.executeJob(job.getId());
          job = managementService.createJobQuery().singleResult();
          assertThat(job.getTenantId()).isEqualTo(newTenant);
  
          // Finish process
          managementService.executeJob(job.getId());
  
          // clean up
          repositoryService.deleteDeployment(deploymentId, true);
      }
  
      public void testHistoryTenancy() {
          if (processEngineConfiguration.getHistoryLevel().isAtLeast(HistoryLevel.AUDIT)) {
              // Generate 3 tasks with tenant
              String processDefinitionIdWithTenant = deployTestProcessWithTestTenant();
              int nrOfProcessInstancesWithTenant = 3;
              for (int i = 0; i < nrOfProcessInstancesWithTenant; i++) {
                  runtimeService.startProcessInstanceById(processDefinitionIdWithTenant);
              }
  
              // Generate 2 tasks without tenant
              String processDefinitionIdNoTenant = deployOneTaskTestProcess();
              int nrOfProcessInstancesNoTenant = 2;
              for (int i = 0; i < nrOfProcessInstancesNoTenant; i++) {
                  runtimeService.startProcessInstanceById(processDefinitionIdNoTenant);
              }
  
              // Complete all tasks
              for (Task task : taskService.createTaskQuery().list()) {
                  taskService.complete(task.getId());
              }
  
              // Verify process instances
              assertThat(
                  historyService
                      .createHistoricProcessInstanceQuery()
                      .processDefinitionId(processDefinitionIdWithTenant)
                      .list()
                      .get(0)
                      .getTenantId()
              ).isEqualTo(TEST_TENANT_ID);
              assertThat(
                  historyService
                      .createHistoricProcessInstanceQuery()
                      .processDefinitionId(processDefinitionIdNoTenant)
                      .list()
                      .get(0)
                      .getTenantId()
              ).isEqualTo("");
              assertThat(historyService.createHistoricProcessInstanceQuery().list()).hasSize(
                  nrOfProcessInstancesWithTenant + nrOfProcessInstancesNoTenant
              );
              assertThat(
                  historyService.createHistoricProcessInstanceQuery().processInstanceTenantId(TEST_TENANT_ID).list()
              ).hasSize(nrOfProcessInstancesWithTenant);
              assertThat(
                  historyService.createHistoricProcessInstanceQuery().processInstanceTenantIdLike("%e%").list()
              ).hasSize(nrOfProcessInstancesWithTenant);
              assertThat(
                  historyService.createHistoricProcessInstanceQuery().processInstanceWithoutTenantId().list()
              ).hasSize(nrOfProcessInstancesNoTenant);
  
              // verify tasks
              assertThat(
                  historyService
                      .createHistoricTaskInstanceQuery()
                      .processDefinitionId(processDefinitionIdWithTenant)
                      .list()
                      .get(0)
                      .getTenantId()
              ).isEqualTo(TEST_TENANT_ID);
              assertThat(
                  historyService
                      .createHistoricTaskInstanceQuery()
                      .processDefinitionId(processDefinitionIdNoTenant)
                      .list()
                      .get(0)
                      .getTenantId()
              ).isEqualTo("");
              assertThat(historyService.createHistoricTaskInstanceQuery().list()).hasSize(
                  nrOfProcessInstancesWithTenant + nrOfProcessInstancesNoTenant
              );
              assertThat(historyService.createHistoricTaskInstanceQuery().taskTenantId(TEST_TENANT_ID).list()).hasSize(
                  nrOfProcessInstancesWithTenant
              );
              assertThat(historyService.createHistoricTaskInstanceQuery().taskTenantIdLike("my%").list()).hasSize(
                  nrOfProcessInstancesWithTenant
              );
              assertThat(historyService.createHistoricTaskInstanceQuery().taskWithoutTenantId().list()).hasSize(
                  nrOfProcessInstancesNoTenant
              );
  
              // verify activities
              List<HistoricActivityInstance> activityInstances = historyService
                  .createHistoricActivityInstanceQuery()
                  .processDefinitionId(processDefinitionIdWithTenant)
                  .list();
              for (HistoricActivityInstance historicActivityInstance : activityInstances) {
                  assertThat(historicActivityInstance.getTenantId()).isEqualTo(TEST_TENANT_ID);
              }
              assertThat(
                  historyService
                      .createHistoricActivityInstanceQuery()
                      .processDefinitionId(processDefinitionIdNoTenant)
                      .list()
                      .get(0)
                      .getTenantId()
              ).isEqualTo("");
              assertThat(historyService.createHistoricActivityInstanceQuery().list()).hasSize(
                  3 * (nrOfProcessInstancesWithTenant + nrOfProcessInstancesNoTenant)
              );
              assertThat(
                  historyService.createHistoricActivityInstanceQuery().activityTenantId(TEST_TENANT_ID).list()
              ).hasSize(3 * nrOfProcessInstancesWithTenant);
              assertThat(historyService.createHistoricActivityInstanceQuery().activityTenantIdLike("my%").list()).hasSize(
                  3 * nrOfProcessInstancesWithTenant
              );
              assertThat(historyService.createHistoricActivityInstanceQuery().activityWithoutTenantId().list()).hasSize(
                  3 * nrOfProcessInstancesNoTenant
              );
          }
      }
  
      public void testProcessDefinitionKeyClashBetweenTenants() {
          String tentanA = "tenantA";
          String tenantB = "tenantB";
  
          // Deploy the same process (same process definition key) for two different tenants.
          String procDefIdA = deployTestProcessWithTestTenant(tentanA);
          String procDefIdB = deployTestProcessWithTestTenant(tenantB);
  
          // verify query
          assertThat(
              repositoryService.createProcessDefinitionQuery().processDefinitionId(procDefIdA).singleResult().getKey()
          ).isEqualTo("oneTaskProcess");
          assertThat(
              repositoryService.createProcessDefinitionQuery().processDefinitionId(procDefIdA).singleResult().getVersion()
          ).isEqualTo(1);
          assertThat(
              repositoryService.createProcessDefinitionQuery().processDefinitionId(procDefIdB).singleResult().getKey()
          ).isEqualTo("oneTaskProcess");
          assertThat(
              repositoryService.createProcessDefinitionQuery().processDefinitionId(procDefIdB).singleResult().getVersion()
          ).isEqualTo(1);
          assertThat(
              repositoryService.createProcessDefinitionQuery().processDefinitionKey("oneTaskProcess").list()
          ).hasSize(2);
          assertThat(
              repositoryService
                  .createProcessDefinitionQuery()
                  .processDefinitionKey("oneTaskProcess")
                  .processDefinitionTenantId(tentanA)
                  .list()
          ).hasSize(1);
          assertThat(
              repositoryService
                  .createProcessDefinitionQuery()
                  .processDefinitionKey("oneTaskProcess")
                  .processDefinitionTenantId(tenantB)
                  .list()
          ).hasSize(1);
          assertThat(
              repositoryService
                  .createProcessDefinitionQuery()
                  .processDefinitionKey("oneTaskProcess")
                  .processDefinitionWithoutTenantId()
                  .list()
          ).hasSize(0);
  
          // Deploy second version
          procDefIdA = deployTestProcessWithTestTenant(tentanA);
  
          assertThat(
              repositoryService.createProcessDefinitionQuery().processDefinitionId(procDefIdA).singleResult().getKey()
          ).isEqualTo("oneTaskProcess");
          assertThat(
              repositoryService.createProcessDefinitionQuery().processDefinitionId(procDefIdA).singleResult().getVersion()
          ).isEqualTo(2);
          assertThat(
              repositoryService
                  .createProcessDefinitionQuery()
                  .processDefinitionKey("oneTaskProcess")
                  .processDefinitionTenantId(tentanA)
                  .latestVersion()
                  .singleResult()
                  .getVersion()
          ).isEqualTo(2);
          assertThat(
              repositoryService.createProcessDefinitionQuery().processDefinitionId(procDefIdB).singleResult().getKey()
          ).isEqualTo("oneTaskProcess");
          assertThat(
              repositoryService.createProcessDefinitionQuery().processDefinitionId(procDefIdB).singleResult().getVersion()
          ).isEqualTo(1);
          assertThat(
              repositoryService.createProcessDefinitionQuery().processDefinitionKey("oneTaskProcess").list().size()
          ).isEqualTo(3);
          assertThat(
              repositoryService
                  .createProcessDefinitionQuery()
                  .processDefinitionKey("oneTaskProcess")
                  .processDefinitionTenantId(tentanA)
                  .list()
                  .size()
          ).isEqualTo(2);
          assertThat(
              repositoryService
                  .createProcessDefinitionQuery()
                  .processDefinitionKey("oneTaskProcess")
                  .processDefinitionTenantId(tenantB)
                  .list()
                  .size()
          ).isEqualTo(1);
          assertThat(
              repositoryService
                  .createProcessDefinitionQuery()
                  .processDefinitionKey("oneTaskProcess")
                  .processDefinitionTenantId(tentanA)
                  .latestVersion()
                  .list()
                  .size()
          ).isEqualTo(1);
          assertThat(
              repositoryService
                  .createProcessDefinitionQuery()
                  .processDefinitionKey("oneTaskProcess")
                  .processDefinitionWithoutTenantId()
                  .list()
                  .size()
          ).isEqualTo(0);
  
          // Now, start process instances by process definition key (no tenant)
          // shouldn't happen, there is no process definition with that
          // key that has no tenant, it has to give an exception as such!
          assertThatExceptionOfType(Exception.class).isThrownBy(() ->
              runtimeService.startProcessInstanceByKey("oneTaskProcess")
          );
  
          ProcessInstance processInstance = runtimeService.startProcessInstanceByKeyAndTenantId(
              "oneTaskProcess",
              tentanA
          );
          assertThat(processInstance.getProcessDefinitionId()).isEqualTo(procDefIdA);
  
          processInstance = runtimeService.startProcessInstanceByKeyAndTenantId("oneTaskProcess", tenantB);
          assertThat(processInstance.getProcessDefinitionId()).isEqualTo(procDefIdB);
      }
  
      public void testSuspendProcessDefinitionTenancy() {
          // Deploy one process definition for tenant A, and two process
          // definitions versions for tenant B
          String tentanA = "tenantA";
          String tenantB = "tenantB";
  
          String procDefIdA = deployTestProcessWithTestTenant(tentanA);
          String procDefIdB = deployTestProcessWithTestTenant(tenantB);
          String procDefIdB2 = deployTestProcessWithTestTenant(tenantB);
  
          // Suspend process definition B
          repositoryService.suspendProcessDefinitionByKey("oneTaskProcess", tenantB);
  
          // Shouldn't be able to start proc defs for tentant B
          try {
              runtimeService.startProcessInstanceById(procDefIdB);
          } catch (ActivitiException e) {}
  
          try {
              runtimeService.startProcessInstanceById(procDefIdB2);
          } catch (ActivitiException e) {}
  
          ProcessInstance processInstance = runtimeService.startProcessInstanceById(procDefIdA);
          assertThat(processInstance).isNotNull();
  
          // Activate process again
          repositoryService.activateProcessDefinitionByKey("oneTaskProcess", tenantB);
  
          processInstance = runtimeService.startProcessInstanceById(procDefIdB);
          assertThat(processInstance).isNotNull();
  
          processInstance = runtimeService.startProcessInstanceById(procDefIdB2);
          assertThat(processInstance).isNotNull();
  
          processInstance = runtimeService.startProcessInstanceById(procDefIdA);
          assertThat(processInstance).isNotNull();
  
          // Suspending with NO tenant id should give an error, cause they both
          // have tenants
          try {
              repositoryService.suspendProcessDefinitionByKey("oneTaskProcess");
          } catch (ActivitiException e) {}
      }
  
      public void testSignalFromProcessTenancy() {
          // Deploy process both with and without tenant
          repositoryService
              .createDeployment()
              .addClasspathResource("org/activiti/engine/test/api/tenant/TenancyTest.testMultiTenancySignals.bpmn20.xml")
              .deploy();
          repositoryService
              .createDeployment()
              .addClasspathResource("org/activiti/engine/test/api/tenant/TenancyTest.testMultiTenancySignals.bpmn20.xml")
              .tenantId(TEST_TENANT_ID)
              .deploy();
  
          // Start 3 proc instances for the one with a tenant and 2 for the one
          // without tenant
          runtimeService.startProcessInstanceByKeyAndTenantId("testMtSignalCatch", TEST_TENANT_ID);
          runtimeService.startProcessInstanceByKeyAndTenantId("testMtSignalCatch", TEST_TENANT_ID);
          runtimeService.startProcessInstanceByKeyAndTenantId("testMtSignalCatch", TEST_TENANT_ID);
          runtimeService.startProcessInstanceByKey("testMtSignalCatch");
          runtimeService.startProcessInstanceByKey("testMtSignalCatch");
  
          // verify
          assertThat(taskService.createTaskQuery().taskName("My task").taskTenantId(TEST_TENANT_ID).count()).isEqualTo(3);
          assertThat(taskService.createTaskQuery().taskName("My task").taskWithoutTenantId().count()).isEqualTo(2);
  
          // Now, start 1 process instance that fires a signal event (not in tenant context), it should only continue those without tenant
          runtimeService.startProcessInstanceByKey("testMtSignalFiring");
          assertThat(
              taskService.createTaskQuery().taskName("Task after signal").taskTenantId(TEST_TENANT_ID).count()
          ).isEqualTo(0);
          assertThat(taskService.createTaskQuery().taskName("Task after signal").taskWithoutTenantId().count()).isEqualTo(
              2
          );
  
          // Start a process instance that is running in tenant context
          runtimeService.startProcessInstanceByKeyAndTenantId("testMtSignalFiring", TEST_TENANT_ID);
          assertThat(
              taskService.createTaskQuery().taskName("Task after signal").taskTenantId(TEST_TENANT_ID).count()
          ).isEqualTo(3);
          assertThat(taskService.createTaskQuery().taskName("Task after signal").taskWithoutTenantId().count()).isEqualTo(
              2
          );
  
          // Cleanup
          for (Deployment deployment : repositoryService.createDeploymentQuery().list()) {
              repositoryService.deleteDeployment(deployment.getId(), true);
          }
      }
  
      public void testSignalThroughApiTenancy() {
          // Deploy process both with and without tenant
          repositoryService
              .createDeployment()
              .addClasspathResource("org/activiti/engine/test/api/tenant/TenancyTest.testMultiTenancySignals.bpmn20.xml")
              .deploy();
          repositoryService
              .createDeployment()
              .addClasspathResource("org/activiti/engine/test/api/tenant/TenancyTest.testMultiTenancySignals.bpmn20.xml")
              .tenantId(TEST_TENANT_ID)
              .deploy();
  
          // Start 4 proc instances for the one with a tenant and 5 for the one without tenant
          runtimeService.startProcessInstanceByKeyAndTenantId("testMtSignalCatch", TEST_TENANT_ID);
          runtimeService.startProcessInstanceByKeyAndTenantId("testMtSignalCatch", TEST_TENANT_ID);
          runtimeService.startProcessInstanceByKeyAndTenantId("testMtSignalCatch", TEST_TENANT_ID);
          runtimeService.startProcessInstanceByKeyAndTenantId("testMtSignalCatch", TEST_TENANT_ID);
          runtimeService.startProcessInstanceByKey("testMtSignalCatch");
          runtimeService.startProcessInstanceByKey("testMtSignalCatch");
          runtimeService.startProcessInstanceByKey("testMtSignalCatch");
          runtimeService.startProcessInstanceByKey("testMtSignalCatch");
          runtimeService.startProcessInstanceByKey("testMtSignalCatch");
  
          // Verify
          assertThat(taskService.createTaskQuery().taskName("My task").taskTenantId(TEST_TENANT_ID).count()).isEqualTo(4);
          assertThat(taskService.createTaskQuery().taskName("My task").taskWithoutTenantId().count()).isEqualTo(5);
  
          // Signal through API (with tenant)
          runtimeService.signalEventReceivedWithTenantId("The Signal", TEST_TENANT_ID);
          assertThat(
              taskService.createTaskQuery().taskName("Task after signal").taskTenantId(TEST_TENANT_ID).count()
          ).isEqualTo(4);
          assertThat(taskService.createTaskQuery().taskName("Task after signal").taskWithoutTenantId().count()).isEqualTo(
              0
          );
  
          // Signal through API (without tenant)
          runtimeService.signalEventReceived("The Signal");
          assertThat(
              taskService.createTaskQuery().taskName("Task after signal").taskTenantId(TEST_TENANT_ID).count()
          ).isEqualTo(4);
          assertThat(taskService.createTaskQuery().taskName("Task after signal").taskWithoutTenantId().count()).isEqualTo(
              5
          );
  
          // Cleanup
          for (Deployment deployment : repositoryService.createDeploymentQuery().list()) {
              repositoryService.deleteDeployment(deployment.getId(), true);
          }
      }
  
      public void testSignalThroughApiTenancyReversed() {
          // cause reversing the
          // order of calling DID leave to an error!
  
          // Deploy process both with and without tenant
          repositoryService
              .createDeployment()
              .addClasspathResource("org/activiti/engine/test/api/tenant/TenancyTest.testMultiTenancySignals.bpmn20.xml")
              .deploy();
          repositoryService
              .createDeployment()
              .addClasspathResource("org/activiti/engine/test/api/tenant/TenancyTest.testMultiTenancySignals.bpmn20.xml")
              .tenantId(TEST_TENANT_ID)
              .deploy();
  
          // Start 4 proc instances for the one with a tenant and 5 for the one without tenant
          runtimeService.startProcessInstanceByKeyAndTenantId("testMtSignalCatch", TEST_TENANT_ID);
          runtimeService.startProcessInstanceByKeyAndTenantId("testMtSignalCatch", TEST_TENANT_ID);
          runtimeService.startProcessInstanceByKeyAndTenantId("testMtSignalCatch", TEST_TENANT_ID);
          runtimeService.startProcessInstanceByKeyAndTenantId("testMtSignalCatch", TEST_TENANT_ID);
          runtimeService.startProcessInstanceByKey("testMtSignalCatch");
          runtimeService.startProcessInstanceByKey("testMtSignalCatch");
          runtimeService.startProcessInstanceByKey("testMtSignalCatch");
          runtimeService.startProcessInstanceByKey("testMtSignalCatch");
          runtimeService.startProcessInstanceByKey("testMtSignalCatch");
  
          // Verify
          assertThat(taskService.createTaskQuery().taskName("My task").taskTenantId(TEST_TENANT_ID).count()).isEqualTo(4);
          assertThat(taskService.createTaskQuery().taskName("My task").taskWithoutTenantId().count()).isEqualTo(5);
  
          // Signal through API (without tenant)
          runtimeService.signalEventReceived("The Signal");
          assertThat(
              taskService.createTaskQuery().taskName("Task after signal").taskTenantId(TEST_TENANT_ID).count()
          ).isEqualTo(0);
          assertThat(taskService.createTaskQuery().taskName("Task after signal").taskWithoutTenantId().count()).isEqualTo(
              5
          );
  
          // Signal through API (with tenant)
          runtimeService.signalEventReceivedWithTenantId("The Signal", TEST_TENANT_ID);
          assertThat(
              taskService.createTaskQuery().taskName("Task after signal").taskTenantId(TEST_TENANT_ID).count()
          ).isEqualTo(4);
          assertThat(taskService.createTaskQuery().taskName("Task after signal").taskWithoutTenantId().count()).isEqualTo(
              5
          );
  
          // Cleanup
          for (Deployment deployment : repositoryService.createDeploymentQuery().list()) {
              repositoryService.deleteDeployment(deployment.getId(), true);
          }
      }
  
      public void testSignalAsyncThroughApiTenancy() {
          // Deploy process both with and without tenant
          repositoryService
              .createDeployment()
              .addClasspathResource("org/activiti/engine/test/api/tenant/TenancyTest.testMultiTenancySignals.bpmn20.xml")
              .deploy();
          repositoryService
              .createDeployment()
              .addClasspathResource("org/activiti/engine/test/api/tenant/TenancyTest.testMultiTenancySignals.bpmn20.xml")
              .tenantId(TEST_TENANT_ID)
              .deploy();
  
          // Start 4 proc instances for the one with a tenant and 5 for the one
          // without tenant
          runtimeService.startProcessInstanceByKeyAndTenantId("testMtSignalCatch", TEST_TENANT_ID);
          runtimeService.startProcessInstanceByKeyAndTenantId("testMtSignalCatch", TEST_TENANT_ID);
          runtimeService.startProcessInstanceByKeyAndTenantId("testMtSignalCatch", TEST_TENANT_ID);
          runtimeService.startProcessInstanceByKeyAndTenantId("testMtSignalCatch", TEST_TENANT_ID);
          runtimeService.startProcessInstanceByKey("testMtSignalCatch");
          runtimeService.startProcessInstanceByKey("testMtSignalCatch");
          runtimeService.startProcessInstanceByKey("testMtSignalCatch");
          runtimeService.startProcessInstanceByKey("testMtSignalCatch");
          runtimeService.startProcessInstanceByKey("testMtSignalCatch");
  
          // Verify
          assertThat(taskService.createTaskQuery().taskName("My task").taskTenantId(TEST_TENANT_ID).count()).isEqualTo(4);
          assertThat(taskService.createTaskQuery().taskName("My task").taskWithoutTenantId().count()).isEqualTo(5);
  
          // Signal through API (with tenant)
          runtimeService.signalEventReceivedAsyncWithTenantId("The Signal", TEST_TENANT_ID);
          assertThat(
              taskService.createTaskQuery().taskName("Task after signal").taskTenantId(TEST_TENANT_ID).count()
          ).isEqualTo(0);
          assertThat(taskService.createTaskQuery().taskName("Task after signal").taskWithoutTenantId().count()).isEqualTo(
              0
          );
  
          for (Job job : managementService.createJobQuery().list()) {
              managementService.executeJob(job.getId());
          }
  
          assertThat(
              taskService.createTaskQuery().taskName("Task after signal").taskTenantId(TEST_TENANT_ID).count()
          ).isEqualTo(4);
          assertThat(taskService.createTaskQuery().taskName("Task after signal").taskWithoutTenantId().count()).isEqualTo(
              0
          );
  
          // Signal through API (without tenant)
          runtimeService.signalEventReceivedAsync("The Signal");
  
          assertThat(
              taskService.createTaskQuery().taskName("Task after signal").taskTenantId(TEST_TENANT_ID).count()
          ).isEqualTo(4);
          assertThat(taskService.createTaskQuery().taskName("Task after signal").taskWithoutTenantId().count()).isEqualTo(
              0
          );
  
          for (Job job : managementService.createJobQuery().list()) {
              managementService.executeJob(job.getId());
          }
  
          assertThat(
              taskService.createTaskQuery().taskName("Task after signal").taskTenantId(TEST_TENANT_ID).count()
          ).isEqualTo(4);
          assertThat(taskService.createTaskQuery().taskName("Task after signal").taskWithoutTenantId().count()).isEqualTo(
              5
          );
  
          // Cleanup
          for (Deployment deployment : repositoryService.createDeploymentQuery().list()) {
              repositoryService.deleteDeployment(deployment.getId(), true);
          }
      }
  
      public void testStartProcessInstanceBySignalTenancy() {
          // Deploy process both with and without tenant
          repositoryService
              .createDeployment()
              .addClasspathResource(
                  "org/activiti/engine/test/api/tenant/TenancyTest.testStartProcessInstanceBySignalTenancy.bpmn20.xml"
              )
              .deploy();
          repositoryService
              .createDeployment()
              .addClasspathResource(
                  "org/activiti/engine/test/api/tenant/TenancyTest.testStartProcessInstanceBySignalTenancy.bpmn20.xml"
              )
              .tenantId(TEST_TENANT_ID)
              .deploy();
  
          // Signaling without tenant
          runtimeService.signalEventReceived("The Signal");
          assertThat(runtimeService.createProcessInstanceQuery().count()).isEqualTo(3);
          assertThat(runtimeService.createProcessInstanceQuery().processInstanceWithoutTenantId().count()).isEqualTo(3);
          assertThat(
              runtimeService.createProcessInstanceQuery().processInstanceTenantId(TEST_TENANT_ID).count()
          ).isEqualTo(0);
  
          // Signalling with tenant
          runtimeService.signalEventReceivedWithTenantId("The Signal", TEST_TENANT_ID);
          assertThat(runtimeService.createProcessInstanceQuery().count()).isEqualTo(6);
          assertThat(runtimeService.createProcessInstanceQuery().processInstanceWithoutTenantId().count()).isEqualTo(3);
          assertThat(
              runtimeService.createProcessInstanceQuery().processInstanceTenantId(TEST_TENANT_ID).count()
          ).isEqualTo(3);
  
          // Start a process instance with a boundary catch (with and without tenant)
          runtimeService.startProcessInstanceByKey("processWithSignalCatch");
          runtimeService.startProcessInstanceByKeyAndTenantId("processWithSignalCatch", TEST_TENANT_ID);
  
          runtimeService.signalEventReceived("The Signal");
          assertThat(runtimeService.createProcessInstanceQuery().count()).isEqualTo(11);
          assertThat(runtimeService.createProcessInstanceQuery().processInstanceWithoutTenantId().count()).isEqualTo(7);
          assertThat(
              runtimeService.createProcessInstanceQuery().processInstanceTenantId(TEST_TENANT_ID).count()
          ).isEqualTo(4);
  
          runtimeService.signalEventReceivedWithTenantId("The Signal", TEST_TENANT_ID);
          assertThat(runtimeService.createProcessInstanceQuery().count()).isEqualTo(14);
          assertThat(runtimeService.createProcessInstanceQuery().processInstanceWithoutTenantId().count()).isEqualTo(7);
          assertThat(
              runtimeService.createProcessInstanceQuery().processInstanceTenantId(TEST_TENANT_ID).count()
          ).isEqualTo(7);
  
          // Cleanup
          for (Deployment deployment : repositoryService.createDeploymentQuery().list()) {
              repositoryService.deleteDeployment(deployment.getId(), true);
          }
      }
  
      public void testStartProcessInstanceByMessageTenancy() {
          // Deploy process both with and without tenant
          repositoryService
              .createDeployment()
              .addClasspathResource("org/activiti/engine/test/api/tenant/TenancyTest.testMessageTenancy.bpmn20.xml")
              .deploy();
          repositoryService
              .createDeployment()
              .addClasspathResource("org/activiti/engine/test/api/tenant/TenancyTest.testMessageTenancy.bpmn20.xml")
              .tenantId(TEST_TENANT_ID)
              .deploy();
  
          // Verify query
          assertThat(
              repositoryService.createProcessDefinitionQuery().messageEventSubscriptionName("My message").count()
          ).isEqualTo(2);
          assertThat(
              repositoryService
                  .createProcessDefinitionQuery()
                  .messageEventSubscriptionName("My message")
                  .processDefinitionWithoutTenantId()
                  .count()
          ).isEqualTo(1);
          assertThat(
              repositoryService
                  .createProcessDefinitionQuery()
                  .messageEventSubscriptionName("My message")
                  .processDefinitionTenantId(TEST_TENANT_ID)
                  .count()
          ).isEqualTo(1);
  
          // Start a process instance by message without tenant
          runtimeService.startProcessInstanceByMessage("My message");
          runtimeService.startProcessInstanceByMessage("My message");
  
          assertThat(taskService.createTaskQuery().taskName("My task").count()).isEqualTo(2);
          assertThat(taskService.createTaskQuery().taskName("My task").taskWithoutTenantId().count()).isEqualTo(2);
          assertThat(taskService.createTaskQuery().taskName("My task").taskTenantId(TEST_TENANT_ID).count()).isEqualTo(0);
  
          // Start a process instance by message with tenant
          runtimeService.startProcessInstanceByMessageAndTenantId("My message", TEST_TENANT_ID);
          runtimeService.startProcessInstanceByMessageAndTenantId("My message", TEST_TENANT_ID);
          runtimeService.startProcessInstanceByMessageAndTenantId("My message", TEST_TENANT_ID);
          assertThat(taskService.createTaskQuery().taskName("My task").count()).isEqualTo(5);
          assertThat(taskService.createTaskQuery().taskName("My task").taskWithoutTenantId().count()).isEqualTo(2);
          assertThat(taskService.createTaskQuery().taskName("My task").taskTenantId(TEST_TENANT_ID).count()).isEqualTo(3);
  
          // Cleanup
          for (Deployment deployment : repositoryService.createDeploymentQuery().list()) {
              repositoryService.deleteDeployment(deployment.getId(), true);
          }
      }
  
      public void testStartProcessInstanceByMessageTenancyReversed() {
          // same as
          // above, but now reversed
  
          // Deploy process both with and without tenant
          repositoryService
              .createDeployment()
              .addClasspathResource("org/activiti/engine/test/api/tenant/TenancyTest.testMessageTenancy.bpmn20.xml")
              .deploy();
          repositoryService
              .createDeployment()
              .addClasspathResource("org/activiti/engine/test/api/tenant/TenancyTest.testMessageTenancy.bpmn20.xml")
              .tenantId(TEST_TENANT_ID)
              .deploy();
  
          // Start a process instance by message with tenant
          runtimeService.startProcessInstanceByMessageAndTenantId("My message", TEST_TENANT_ID);
          runtimeService.startProcessInstanceByMessageAndTenantId("My message", TEST_TENANT_ID);
          runtimeService.startProcessInstanceByMessageAndTenantId("My message", TEST_TENANT_ID);
          assertThat(taskService.createTaskQuery().taskName("My task").count()).isEqualTo(3);
          assertThat(taskService.createTaskQuery().taskName("My task").taskWithoutTenantId().count()).isEqualTo(0);
          assertThat(taskService.createTaskQuery().taskName("My task").taskTenantId(TEST_TENANT_ID).count()).isEqualTo(3);
  
          // Start a process instance by message without tenant
          runtimeService.startProcessInstanceByMessage("My message");
          runtimeService.startProcessInstanceByMessage("My message");
  
          assertThat(taskService.createTaskQuery().taskName("My task").count()).isEqualTo(5);
          assertThat(taskService.createTaskQuery().taskName("My task").taskWithoutTenantId().count()).isEqualTo(2);
          assertThat(taskService.createTaskQuery().taskName("My task").taskTenantId(TEST_TENANT_ID).count()).isEqualTo(3);
  
          // Cleanup
          for (Deployment deployment : repositoryService.createDeploymentQuery().list()) {
              repositoryService.deleteDeployment(deployment.getId(), true);
          }
      }
  
      // Bug from http://forums.activiti.org/content/callactiviti-tenant-id
      public void testCallActivityWithTenant() {
          if (processEngineConfiguration.getHistoryLevel().isAtLeast(HistoryLevel.ACTIVITY)) {
              String tenantId = "apache";
  
              // deploying both processes. Process 1 will call Process 2
              repositoryService
                  .createDeployment()
                  .addClasspathResource(
                      "org/activiti/engine/test/api/tenant/TenancyTest.testCallActivityWithTenant-process01.bpmn20.xml"
                  )
                  .tenantId(tenantId)
                  .deploy();
              repositoryService
                  .createDeployment()
                  .addClasspathResource(
                      "org/activiti/engine/test/api/tenant/TenancyTest.testCallActivityWithTenant-process02.bpmn20.xml"
                  )
                  .tenantId(tenantId)
                  .deploy();
  
              // Starting Process 1. Process 1 will be executed successfully but
              // when the call to process 2 is made internally it will throw the
              // exception
              ProcessInstance processInstance = runtimeService.startProcessInstanceByKeyAndTenantId(
                  "process1",
                  null,
                  singletonMap("sendFor", "test"),
                  tenantId
              );
              assertThat(processInstance).isNotNull();
  
              assertThat(
                  historyService
                      .createHistoricProcessInstanceQuery()
                      .processDefinitionKey("process2")
                      .processInstanceTenantId(tenantId)
                      .count()
              ).isEqualTo(1);
              assertThat(
                  historyService.createHistoricProcessInstanceQuery().processDefinitionKey("process2").count()
              ).isEqualTo(1);
  
              // following line if executed will give activiti object not found
              // exception as the process1 is linked to a tenant id.
              assertThatExceptionOfType(Exception.class).isThrownBy(() ->
                  runtimeService.startProcessInstanceByKey("process1")
              );
  
              // Cleanup
              for (Deployment deployment : repositoryService.createDeploymentQuery().list()) {
                  repositoryService.deleteDeployment(deployment.getId(), true);
              }
          }
      }
  
      /*
       * See https://activiti.atlassian.net/browse/ACT-4034
       */
      public void testGetLatestProcessDefinitionVersionForSameProcessDefinitionKey() {
          String tenant1 = "tenant1";
          String tenant2 = "tenant2";
  
          // Tenant 1 ==> version 4
          for (int i = 0; i < 4; i++) {
              deployTestProcessWithTestTenant(tenant1);
          }
  
          // Tenant 2 ==> version 2
          for (int i = 0; i < 2; i++) {
              deployTestProcessWithTestTenant(tenant2);
          }
  
          // No tenant ==> version 3
          for (int i = 0; i < 3; i++) {
              deployTestProcessWithTwoTasksNoTenant();
          }
  
          ProcessDefinition processDefinition = repositoryService
              .createProcessDefinitionQuery()
              .processDefinitionTenantId(tenant1)
              .latestVersion()
              .singleResult();
          assertThat(processDefinition.getVersion()).isEqualTo(4);
  
          processDefinition = repositoryService
              .createProcessDefinitionQuery()
              .processDefinitionTenantId(tenant2)
              .latestVersion()
              .singleResult();
          assertThat(processDefinition.getVersion()).isEqualTo(2);
  
          processDefinition = repositoryService
              .createProcessDefinitionQuery()
              .processDefinitionWithoutTenantId()
              .latestVersion()
              .singleResult();
          assertThat(processDefinition.getVersion()).isEqualTo(3);
  
          List<ProcessDefinition> processDefinitions = repositoryService
              .createProcessDefinitionQuery()
              .latestVersion()
              .list();
          assertThat(processDefinitions.size()).isEqualTo(3);
  
          // Verify they have different tenant ids
          int tenant1Count = 0,
              tenant2Count = 0,
              noTenantCount = 0;
          for (ProcessDefinition p : processDefinitions) {
              if (p.getTenantId() == null || p.getTenantId().equals(ProcessEngineConfiguration.NO_TENANT_ID)) {
                  noTenantCount++;
              } else if (p.getTenantId().equals(tenant1)) {
                  tenant1Count++;
              } else if (p.getTenantId().equals(tenant2)) {
                  tenant2Count++;
              }
          }
          assertThat(tenant1Count).isEqualTo(1);
          assertThat(tenant2Count).isEqualTo(1);
          assertThat(noTenantCount).isEqualTo(1);
      }
  }
  