content: /*
   * Copyright 2017-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.cloud.qa.story;
  
  import static org.activiti.cloud.acc.core.helper.Filters.checkProcessInstances;
  import static org.activiti.cloud.qa.helpers.ProcessDefinitionRegistry.processDefinitionKeyMatcher;
  import static org.activiti.cloud.qa.helpers.ProcessDefinitionRegistry.processDefinitionKeys;
  import static org.activiti.cloud.qa.helpers.ProcessDefinitionRegistry.withTasks;
  import static org.assertj.core.api.Assertions.assertThat;
  import static org.assertj.core.api.Assertions.tuple;
  import static org.awaitility.Awaitility.await;
  
  import feign.FeignException;
  import java.io.IOException;
  import java.util.ArrayList;
  import java.util.Collection;
  import java.util.HashMap;
  import java.util.Iterator;
  import java.util.List;
  import java.util.Map;
  import java.util.stream.Collectors;
  import net.serenitybdd.core.Serenity;
  import net.thucydides.core.annotations.Steps;
  import org.activiti.api.model.shared.event.VariableEvent;
  import org.activiti.api.process.model.ProcessDefinition;
  import org.activiti.api.process.model.ProcessInstance;
  import org.activiti.api.process.model.events.ProcessRuntimeEvent;
  import org.activiti.api.task.model.Task;
  import org.activiti.api.task.model.builders.TaskPayloadBuilder;
  import org.activiti.api.task.model.events.TaskRuntimeEvent;
  import org.activiti.cloud.acc.core.steps.audit.AuditSteps;
  import org.activiti.cloud.acc.core.steps.audit.admin.AuditAdminSteps;
  import org.activiti.cloud.acc.core.steps.query.ProcessQuerySteps;
  import org.activiti.cloud.acc.core.steps.query.TaskQuerySteps;
  import org.activiti.cloud.acc.core.steps.query.admin.ProcessQueryAdminSteps;
  import org.activiti.cloud.acc.core.steps.runtime.ProcessRuntimeBundleSteps;
  import org.activiti.cloud.acc.core.steps.runtime.TaskRuntimeBundleSteps;
  import org.activiti.cloud.acc.core.steps.runtime.admin.ProcessRuntimeAdminSteps;
  import org.activiti.cloud.acc.core.steps.runtime.admin.TaskRuntimeAdminSteps;
  import org.activiti.cloud.acc.shared.rest.error.ExpectRestNotFound;
  import org.activiti.cloud.api.model.shared.events.CloudRuntimeEvent;
  import org.activiti.cloud.api.process.model.CloudProcessDefinition;
  import org.activiti.cloud.api.process.model.CloudProcessInstance;
  import org.activiti.cloud.api.task.model.CloudTask;
  import org.jbehave.core.annotations.Given;
  import org.jbehave.core.annotations.Then;
  import org.jbehave.core.annotations.When;
  import org.jbehave.core.model.ExamplesTable;
  import org.springframework.hateoas.PagedModel;
  
  @SuppressWarnings("java:S5960")
  public class ProcessInstanceTasks {
  
      @Steps
      private ProcessRuntimeBundleSteps processRuntimeBundleSteps;
  
      @Steps
      private TaskRuntimeBundleSteps taskRuntimeBundleSteps;
  
      @Steps
      private TaskRuntimeAdminSteps taskRuntimeAdminSteps;
  
      @Steps
      private ProcessRuntimeAdminSteps processRuntimeAdminSteps;
  
      @Steps
      private ProcessQuerySteps processQuerySteps;
  
      @Steps
      private TaskQuerySteps taskQuerySteps;
  
      @Steps
      private ProcessQueryAdminSteps processQueryAdminSteps;
  
      @Steps
      private AuditSteps auditSteps;
  
      @Steps
      private AuditAdminSteps auditAdminSteps;
  
      private ProcessInstance processInstance;
  
      private String processInstanceDiagram;
  
      private Task currentTask;
      private String processInstanceAdminDiagram;
  
      private static int AUDIT_STEP_TIMEOUT = 60;
  
      @When("services are started")
      public void checkServicesStatus() {
          processRuntimeBundleSteps.checkServicesHealth();
          taskRuntimeBundleSteps.checkServicesHealth();
          processRuntimeAdminSteps.checkServicesHealth();
          processQuerySteps.checkServicesHealth();
          taskQuerySteps.checkServicesHealth();
          processQueryAdminSteps.checkServicesHealth();
          auditSteps.checkServicesHealth();
          auditAdminSteps.checkServicesHealth();
      }
  
      @When("the user starts with variables for $processName with variables $variableName1 and $variableName2")
      public void startProcess(String processName, String variableName1, String variableName2) {
          Map<String, Object> variables = new HashMap<>();
          variables.put(variableName1, variableName1); //using var names as values
          variables.put(variableName2, variableName2);
  
          processInstance =
              processRuntimeBundleSteps.startProcessWithVariables(processDefinitionKeyMatcher(processName), variables);
          checkProcessWithTaskCreated(processName);
      }
  
      @When("the user starts an instance of the process called $processName")
      public void startProcess(String processName) {
          String processDefinitionKey = processDefinitionKeyMatcher(processName);
          startProcessByKey(processDefinitionKey);
          checkProcessWithTaskCreated(processName);
      }
  
      @When("the user starts an instance of the process with key $processDefinitionKey")
      public void startProcessByKey(String processDefinitionKey) {
          processInstance = processRuntimeBundleSteps.startProcess(processDefinitionKey);
  
          Serenity.setSessionVariable("processInstanceId").to(processInstance.getId());
      }
  
      @When("the user starts a process with variables called $processName")
      public void startProcessWithVariables(String processName) throws IOException {
          String processDefinitionKey = processDefinitionKeyMatcher(processName);
          processInstance = processRuntimeBundleSteps.startProcess(processDefinitionKey, true);
  
          Serenity.setSessionVariable("processInstanceId").to(processInstance.getId());
          checkProcessWithTaskCreated(processName);
      }
  
      private void checkProcessWithTaskCreated(String processName) {
          assertThat(processInstance).isNotNull();
  
          if (withTasks(processName)) {
              List<Task> tasks = new ArrayList<>(
                  processRuntimeBundleSteps.getTaskByProcessInstanceId(processInstance.getId())
              );
              assertThat(tasks).isNotEmpty();
              currentTask = tasks.get(0);
              assertThat(currentTask).isNotNull();
              Serenity.setSessionVariable("currentTaskId").to(currentTask.getId());
          }
  
          Serenity.setSessionVariable("processInstanceId").to(processInstance.getId());
      }
  
      @Given("any suspended process instance")
      public void suspendCurrentProcessInstance() {
          this.startProcess("SIMPLE_PROCESS_INSTANCE");
          processRuntimeBundleSteps.suspendProcessInstance(processInstance.getId());
      }
  
      @When("the assignee is $user")
      @Then("the assignee is $user")
      public void checkAssignee(String user) throws Exception {
          assertThat(taskRuntimeBundleSteps.getTaskById(currentTask.getId()).getAssignee()).isEqualTo(user);
      }
  
      @When("the user claims the task")
      public void claimTask() throws Exception {
          taskRuntimeBundleSteps.claimTask(currentTask.getId());
      }
  
      @When("the user assign the task to $assignee")
      public void assignTask(String assignee) throws Exception {
          taskRuntimeBundleSteps.assignTask(
              currentTask.getId(),
              TaskPayloadBuilder.assign().withTaskId(currentTask.getId()).withAssignee(assignee).build()
          );
      }
  
      @Then("the user cannot assign the task to $user")
      public void cannotAssignTask(String user) throws Exception {
          taskRuntimeBundleSteps.cannotAssignTask(
              currentTask.getId(),
              TaskPayloadBuilder.assign().withTaskId(currentTask.getId()).withAssignee(user).build()
          );
      }
  
      @When("the user releases the task")
      public void releaseTask() throws Exception {
          taskRuntimeBundleSteps.releaseTask(currentTask.getId());
      }
  
      @When("the user completes the task")
      public void completeTask() throws Exception {
          taskRuntimeBundleSteps.completeTask(
              currentTask.getId(),
              TaskPayloadBuilder.complete().withTaskId(currentTask.getId()).build()
          );
      }
  
      @When("the user completes the task with variable $variableName set to $value")
      public void completeTask(String variableName, String value) throws Exception {
          taskRuntimeBundleSteps.completeTask(
              currentTask.getId(),
              TaskPayloadBuilder.complete().withTaskId(currentTask.getId()).withVariable(variableName, value).build()
          );
      }
  
      @Then("the process instance reaches a task named $taskName")
      public void checkProcessIsOnTask(String taskName) {
          await()
              .untilAsserted(() -> {
                  Collection<CloudTask> tasks = processRuntimeBundleSteps.getTaskByProcessInstanceId(
                      processInstance.getId()
                  );
                  assertThat(tasks).extracting(CloudTask::getName).contains("Wait");
              });
      }
  
      @Then(
          "the process instance has a resultCollection named $resultCollectionName with entries of size $entriesSize as following: $variableTable"
      )
      public void checkProcessHasResultCollection(
          String resultCollectionName,
          int entriesSize,
          ExamplesTable variableTable
      ) {
          List<Object> resultCollection = new ArrayList<>();
          Map<String, Object> resultCollectionEntry = new HashMap<>();
          Iterator<Map<String, String>> iterator = variableTable.getRows().iterator();
          int currentCount = 1;
          while (iterator.hasNext()) {
              Map<String, String> currentRow = iterator.next();
              resultCollectionEntry.put(currentRow.get("name"), currentRow.get("value"));
              if (currentCount % entriesSize == 0) {
                  resultCollection.add(resultCollectionEntry);
                  resultCollectionEntry = new HashMap<>();
              }
              currentCount++;
          }
  
          processQuerySteps.checkProcessInstanceHasVariableValue(
              processInstance.getId(),
              resultCollectionName,
              resultCollection
          );
      }
  
      @When(
          "the user completes the task available in the current process instance passing the following variables: $variableTable"
      )
      public void completeTask(ExamplesTable variableTable) {
          Map<String, Object> variables = new HashMap<>();
          variableTable.getRows().forEach(map -> variables.put(map.get("name"), map.get("value")));
          Collection<CloudTask> tasks = processRuntimeBundleSteps.getTaskByProcessInstanceId(processInstance.getId());
          assertThat(tasks).isNotEmpty();
          taskRuntimeBundleSteps.completeTask(
              tasks.iterator().next().getId(),
              TaskPayloadBuilder.complete().withVariables(variables).build()
          );
      }
  
      @When("the admin completes the task")
      public void adminCompleteTask() throws Exception {
          taskRuntimeAdminSteps.completeTask(
              currentTask.getId(),
              TaskPayloadBuilder.complete().withTaskId(currentTask.getId()).build()
          );
      }
  
      @Then("the user cannot complete the task")
      public void cannotCompleteTask() throws Exception {
          taskRuntimeBundleSteps.cannotCompleteTask(
              currentTask.getId(),
              TaskPayloadBuilder.complete().withTaskId(currentTask.getId()).build()
          );
      }
  
      @When("the status of the task since the beginning is $status")
      public void checkTaskStatusSinceBeginning(Task.TaskStatus status) {
          taskRuntimeBundleSteps.checkTaskStatus(currentTask.getId(), status);
          taskQuerySteps.checkTaskStatus(currentTask.getId(), status);
          auditSteps.checkTaskCreatedAndAssignedEventsWhenAlreadyAssigned(currentTask.getId());
      }
  
      @When("the user saves the task with variable $variableName equal to $variableValue")
      public void saveTask(String variableName, String variableValue) throws Exception {
          taskRuntimeBundleSteps.saveTask(
              currentTask.getId(),
              TaskPayloadBuilder.save().withTaskId(currentTask.getId()).withVariable(variableName, variableValue).build()
          );
      }
  
      @When("the status of the task is $taskStatus")
      public void checkTaskStatus(Task.TaskStatus taskStatus) throws Exception {
          taskRuntimeBundleSteps.checkTaskStatus(currentTask.getId(), taskStatus);
          taskQuerySteps.checkTaskStatus(currentTask.getId(), taskStatus);
  
          switch (taskStatus) {
              case CREATED:
                  auditSteps.checkTaskCreatedEvent(currentTask.getId());
                  break;
              case ASSIGNED:
                  auditSteps.checkTaskCreatedAndAssignedEvents(currentTask.getId());
                  break;
              case COMPLETED:
                  auditSteps.checkTaskCreatedAndAssignedAndCompletedEvents(currentTask.getId());
                  break;
          }
      }
  
      @Then("the task cannot be claimed by user")
      public void cannotClaimTask() throws Exception {
          taskRuntimeBundleSteps.cannotClaimTask(currentTask.getId());
          //the claimed task shouldn't be found by query
          Collection<? extends Task> tasks = taskQuerySteps.getAllTasks().getContent();
          assertThat(tasks).extracting("id").doesNotContain(currentTask.getId());
      }
  
      @Then("tasks of $processName cannot be seen by user")
      public void cannotSeeTasksOfDefinition(String processName) throws Exception {
          Collection<? extends Task> tasks = taskQuerySteps.getAllTasks().getContent();
          assertThat(tasks).extracting("processDefinitionId").doesNotContain(processDefinitionKeyMatcher(processName));
      }
  
      @Then("the status of the process and the task is changed to completed")
      public void verifyProcessAndTasksStatus() throws Exception {
          processQuerySteps.checkProcessInstanceStatus(
              processInstance.getId(),
              ProcessInstance.ProcessInstanceStatus.COMPLETED
          );
          auditSteps.checkProcessInstanceTaskEvent(
              processInstance.getId(),
              currentTask.getId(),
              TaskRuntimeEvent.TaskEvents.TASK_COMPLETED
          );
          //the process instance disappears once it is completed
          processRuntimeBundleSteps.checkProcessInstanceNotFound(processInstance.getId());
      }
  
      @Then("the status of the process is changed to completed")
      public void verifyProcessStatusCompleted() throws Exception {
          String processId = Serenity.sessionVariableCalled("processInstanceId");
          processQuerySteps.checkProcessInstanceStatus(processId, ProcessInstance.ProcessInstanceStatus.COMPLETED);
          auditSteps.checkProcessInstanceEvent(
              processId,
              ProcessRuntimeEvent.ProcessEvents.PROCESS_COMPLETED,
              AUDIT_STEP_TIMEOUT
          );
      }
  
      @Then("the status of the process is changed to suspended")
      public void verifyProcessStatusSuspended() throws Exception {
          String processId = Serenity.sessionVariableCalled("processInstanceId");
          processQuerySteps.checkProcessInstanceStatus(processId, ProcessInstance.ProcessInstanceStatus.SUSPENDED);
          auditSteps.checkProcessInstanceEvent(
              processId,
              ProcessRuntimeEvent.ProcessEvents.PROCESS_SUSPENDED,
              AUDIT_STEP_TIMEOUT
          );
      }
  
      @Then("the status of the process is changed to running")
      public void verifyProcessStatusResumed() throws Exception {
          String processId = Serenity.sessionVariableCalled("processInstanceId");
          processQuerySteps.checkProcessInstanceStatus(processId, ProcessInstance.ProcessInstanceStatus.RUNNING);
          auditSteps.checkProcessInstanceEvent(
              processId,
              ProcessRuntimeEvent.ProcessEvents.PROCESS_RESUMED,
              AUDIT_STEP_TIMEOUT
          );
      }
  
      @Then("a variable was created with name $variableName")
      @When("a variable was created with name $variableName")
      public void verifyVariableCreated(String variableName) throws Exception {
          String processId = Serenity.sessionVariableCalled("processInstanceId");
  
          processQuerySteps.checkProcessInstanceHasVariable(processId, variableName);
          auditSteps.checkProcessInstanceVariableEvent(
              processId,
              variableName,
              VariableEvent.VariableEvents.VARIABLE_CREATED
          );
      }
  
      @When("the user deletes the process")
      public void deleteCurrentProcessInstance() throws Exception {
          processRuntimeBundleSteps.deleteProcessInstance(processInstance.getId());
      }
  
      @When("the admin deletes the process")
      public void adminDeleteCurrentProcessInstance() throws Exception {
          processRuntimeAdminSteps.deleteProcessInstance(processInstance.getId());
      }
  
      @When("the admin force destroys the process")
      public void adminForceDeleteCurrentProcessInstance() throws Exception {
          processRuntimeAdminSteps.destroyProcessInstance(processInstance.getId(), true);
      }
  
      @When("the user suspends the process instance")
      public void suspendProcessInstance() throws Exception {
          processRuntimeBundleSteps.suspendProcessInstance(processInstance.getId());
      }
  
      @Then("the process instance is deleted")
      public void verifyProcessInstanceIsDeleted() throws Exception {
          //TODO change to DELETED status and PROCESS_DELETED event when RB is ready
          processRuntimeBundleSteps.checkProcessInstanceNotFound(processInstance.getId());
          processQuerySteps.checkProcessInstanceStatus(
              processInstance.getId(),
              ProcessInstance.ProcessInstanceStatus.CANCELLED
          );
          auditSteps.checkProcessInstanceEvent(
              processInstance.getId(),
              ProcessRuntimeEvent.ProcessEvents.PROCESS_CANCELLED
          );
      }
  
      @Then("the process instance is destroyed")
      public void verifyProcessInstanceIsForceDestroyed() throws Exception {
          processRuntimeBundleSteps.checkProcessInstanceNotFound(processInstance.getId());
          processQuerySteps.checkProcessInstanceNotFound(processInstance.getId());
          auditSteps.checkProcessInstanceEvent(
              processInstance.getId(),
              ProcessRuntimeEvent.ProcessEvents.PROCESS_DELETED
          );
      }
  
      @When("open the process diagram")
      public void openProcessInstanceDiagram() {
          processInstanceDiagram = processRuntimeBundleSteps.openProcessInstanceDiagram(processInstance.getId());
      }
  
      @Then("the diagram is shown")
      public void checkProcessInstanceDiagram() throws Exception {
          processRuntimeBundleSteps.checkProcessInstanceDiagram(processInstanceDiagram);
      }
  
      @Then("no diagram is shown")
      public void checkProcessInstanceNoDiagram() throws Exception {
          processRuntimeBundleSteps.checkProcessInstanceNoDiagram(processInstanceDiagram);
      }
  
      @When("query the process diagram")
      public void queryProcessInstanceDiagram() {
          processInstanceDiagram = processQuerySteps.getProcessInstanceDiagram(processInstance.getId());
      }
  
      @When("query the process diagram admin endpoint")
      public void queryProcessInstanceDiagramAdmin() {
          processInstanceAdminDiagram = processQueryAdminSteps.getProcessInstanceDiagram(processInstance.getId());
      }
  
      @Then("query the process diagram admin endpoint is unauthorized")
      public void queryProcessInstanceDiagramAdminUnauthorized() {
          try {
              processInstanceAdminDiagram = processQueryAdminSteps.getProcessInstanceDiagram(processInstance.getId());
          } catch (FeignException.Forbidden expected) {
              assertThat(expected.status()).isEqualTo(403);
              return;
          }
  
          throw new AssertionError("fail");
      }
  
      @Then("the query diagram is shown in admin endpoint")
      public void checkQueryProcessInstanceDiagramAdmin() throws Exception {
          processQueryAdminSteps.checkProcessInstanceDiagram(processInstanceAdminDiagram);
      }
  
      @Then("no query diagram is shown in admin endpoint")
      public void checkQueryProcessInstanceNoDiagramAdmin() throws Exception {
          processQueryAdminSteps.checkProcessInstanceNoDiagram(processInstanceAdminDiagram);
      }
  
      @Then("the query diagram is shown")
      public void checkQueryProcessInstanceDiagram() throws Exception {
          processQuerySteps.checkProcessInstanceDiagram(processInstanceDiagram);
      }
  
      @Then("no query diagram is shown")
      public void checkQueryProcessInstanceNoDiagram() throws Exception {
          processQuerySteps.checkProcessInstanceNoDiagram(processInstanceDiagram);
      }
  
      @When("activate the process")
      public void activateCurrentProcessInstance() {
          processRuntimeBundleSteps.resumeProcessInstance(processInstance.getId());
      }
  
      @Then("the process cannot be activated anymore")
      @ExpectRestNotFound("Unable to find process instance for the given id")
      public void cannotActivateProcessInstance() {
          processRuntimeBundleSteps.resumeProcessInstance(processInstance.getId());
      }
  
      @Then("the user is able to resume the process instance")
      public void activateProcessInstance() {
          processRuntimeBundleSteps.resumeProcessInstance(processInstance.getId());
      }
  
      @Then("the user can get events for process with variables instances in admin endpoint")
      public void checkIfEventsFromProcessesWithVariablesArePresentAdmin() {
          assertThat(processInstance).isNotNull();
          await()
              .untilAsserted(() -> {
                  Collection<CloudRuntimeEvent> cloudRuntimeEvents = auditAdminSteps.getEventsAdmin().getContent();
                  assertThat(cloudRuntimeEvents)
                      .extracting(CloudRuntimeEvent::getProcessInstanceId, CloudRuntimeEvent::getProcessDefinitionKey)
                      .contains(tuple(processInstance.getId(), "ProcessWithVariables"));
              });
      }
  
      @Then("the user can query process with variables instances in admin endpoints")
      public void checkIfProcessWithVariablesArePresentQueryAdmin() {
          assertThat(
              checkProcessInstances(
                  processQueryAdminSteps.getAllProcessInstancesAdmin(),
                  processDefinitionKeys.get("PROCESS_INSTANCE_WITH_VARIABLES")
              )
          )
              .isNotEmpty();
      }
  
      @Then("the user can get process with variables instances in admin endpoint")
      public void checkIfProcessWithVariablesArePresentAdmin() {
          assertThat(
              checkProcessInstances(
                  processRuntimeAdminSteps.getProcessInstances(),
                  processDefinitionKeys.get("PROCESS_INSTANCE_WITH_VARIABLES")
              )
          )
              .isNotEmpty();
      }
  
      @Then("the task from $processName is $status and it is called $taskName")
      public void checkTaskFromProcessInstance(String processName, Task.TaskStatus status, String taskName) {
          List<ProcessInstance> processInstancesList = new ArrayList<>(
              processRuntimeBundleSteps.getAllProcessInstances()
          );
          assertThat(processInstancesList)
              .extracting("processDefinitionKey")
              .contains(processDefinitionKeyMatcher(processName));
  
          //filter the list
          processInstancesList =
              processInstancesList
                  .stream()
                  .filter(p -> p.getProcessDefinitionKey().equals(processDefinitionKeyMatcher(processName)))
                  .collect(Collectors.toList());
          assertThat(processInstancesList.size()).isEqualTo(1);
  
          List<Task> tasksList = new ArrayList<>(
              processRuntimeBundleSteps.getTaskByProcessInstanceId(processInstancesList.get(0).getId())
          );
  
          assertThat(tasksList).isNotEmpty();
          currentTask = tasksList.get(0);
          assertThat(currentTask.getStatus()).isEqualTo(status);
          assertThat(currentTask.getName()).isEqualTo(taskName);
      }
  
      @When("the user gets the process definitions")
      public void getProcessDefinitions() {
          Collection<ProcessDefinition> processDefinitionsRuntimeBundle = processRuntimeBundleSteps.getProcessDefinitions();
          Collection<ProcessDefinition> processDefinitionsQuery = processQuerySteps.getProcessDefinitions().getContent();
  
          Serenity.setSessionVariable("processDefinitionsRuntimeBundle").to(processDefinitionsRuntimeBundle);
          Serenity.setSessionVariable("processDefinitionsQuery").to(processDefinitionsQuery);
      }
  
      @Then("all the process definitions are present")
      public void checkProcessDefinitions() {
          Collection<ProcessDefinition> processDefinitionsRuntimeBundle = Serenity.sessionVariableCalled(
              "processDefinitionsRuntimeBundle"
          );
          Collection<ProcessDefinition> processDefinitionsQuery = Serenity.sessionVariableCalled(
              "processDefinitionsQuery"
          );
          assertThat(processDefinitionsRuntimeBundle).extracting("key").containsAll(processDefinitionKeys.values());
          assertThat(processDefinitionsQuery).extracting("key").containsAll(processDefinitionKeys.values());
      }
  
      @Then("the $processName definition has the $field field with value $value")
      public void checkIfFieldIsPresentAndHasValue(String processName, String field, String value) {
          ProcessDefinition processDefinition = processRuntimeBundleSteps.getProcessDefinitionByKey(
              processDefinitionKeyMatcher(processName)
          );
          assertThat(processDefinition).extracting(field).isEqualTo(value);
      }
  
      @Then("The user gets all the process definitions in admin endpoint")
      public void checkCanGetProcessDefinitionsAsAdmin() {
          PagedModel<CloudProcessDefinition> processDefinitions = processQueryAdminSteps.getAllProcessDefinitions();
          assertThat(processDefinitions.getContent())
              .isNotNull()
              .extracting(CloudProcessDefinition::getName)
              .contains("single-task", "Process with variables", "ConnectorProcess");
      }
  
      @Then("the process instance is updated")
      public void checkIfTaskUpdated() {
          auditSteps.checkProcessInstanceUpdatedEvent(processInstance.getId());
      }
  
      @When("the user updates the name of the process instance to $newProcessName")
      public void setTaskName(String newProcessName) {
          processInstance = processRuntimeBundleSteps.setProcessName(processInstance.getId(), newProcessName);
      }
  
      @Then("the process has the name $newProcessName")
      public void checkProccessInstanceName(String newProcessName) {
          assertThat(processRuntimeBundleSteps.getProcessInstanceById(processInstance.getId()).getName())
              .isEqualTo(newProcessName);
  
          // propagation my take some time to reach query
          await()
              .untilAsserted(() ->
                  assertThat(processQuerySteps.getProcessInstance(processInstance.getId()).getName())
                      .isEqualTo(newProcessName)
              );
      }
  
      @When("the user set a process instance name $myProcessInstanceName and starts the process $processName")
      public void startProcessWithProcessInstanceName(String myProcessInstanceName, String processName) {
          processInstance =
              processRuntimeBundleSteps.startProcessWithProcessInstanceName(
                  processDefinitionKeyMatcher(processName),
                  myProcessInstanceName
              );
      }
  
      @Then("verify the process instance name is $myProcessInstanceName")
      public void verifyTheProcessInstanceNameIsTheOneSupplied(String myProcessInstanceName) {
          processRuntimeBundleSteps.checkProcessInstanceName(processInstance.getId(), myProcessInstanceName);
          processQuerySteps.checkProcessInstanceName(processInstance.getId(), myProcessInstanceName);
      }
  
      @Then("the task has the completion fields set")
      public void verifyTheCorrectCompletionFieldsAreSet() {
          await()
              .untilAsserted(() -> {
                  Task queriedTask = taskQuerySteps.getTaskById(currentTask.getId());
  
                  assertThat(queriedTask.getCompletedDate()).isNotNull();
                  assertThat(queriedTask.getDuration()).isNotNull();
  
                  List<CloudRuntimeEvent> taskCompletedEvents = auditSteps
                      .getEventsByEntityId(currentTask.getId())
                      .stream()
                      .filter(cloudRuntimeEvent ->
                          cloudRuntimeEvent.getEventType().equals(TaskRuntimeEvent.TaskEvents.TASK_COMPLETED)
                      )
                      .collect(Collectors.toList());
  
                  assertThat(taskCompletedEvents.get(0).getEventType())
                      .isEqualTo(TaskRuntimeEvent.TaskEvents.TASK_COMPLETED);
                  assertThat(queriedTask.getCompletedDate().getTime())
                      .isEqualTo(taskCompletedEvents.get(0).getTimestamp());
                  assertThat(queriedTask.getDuration())
                      .isEqualTo(taskCompletedEvents.get(0).getTimestamp() - queriedTask.getCreatedDate().getTime());
              });
      }
  
      @Then("the generated events have sequence number set")
      public void verifyEventSequenceNumberIsSet() {
          String processId = Serenity.sessionVariableCalled("processInstanceId");
          Collection<CloudRuntimeEvent> generatedEvents = auditSteps.getEventsByEntityId(processId);
          List<Integer> sequenceNumbers = generatedEvents
              .stream()
              .map(CloudRuntimeEvent::getSequenceNumber)
              .collect(Collectors.toList());
          for (int i = 0; i <= generatedEvents.size() - 1; i++) {
              assertThat(sequenceNumbers).contains(i);
          }
      }
  
      @Then("the generated events have the same message id")
      public void verifyEventMessageIdIsSet() {
          String processId = Serenity.sessionVariableCalled("processInstanceId");
          await()
              .untilAsserted(() -> {
                  Collection<CloudRuntimeEvent> generatedEvents = auditSteps.getEventsByProcessInstanceId(processId);
  
                  CloudRuntimeEvent cloudRuntimeEvent = generatedEvents.stream().findFirst().orElse(null);
                  assertThat(cloudRuntimeEvent).isNotNull();
                  generatedEvents.forEach(event ->
                      assertThat(event.getMessageId()).isEqualTo(cloudRuntimeEvent.getMessageId())
                  );
              });
      }
  
      @Then("the process instance can be queried using LIKE operator")
      public void queryProcessByNameNameWithLikeOperator() {
          PagedModel<CloudProcessInstance> retrievedProcesses = processQuerySteps.getProcessInstancesByName(
              processInstance.getName().substring(0, 2)
          );
          for (ProcessInstance process : retrievedProcesses) {
              assertThat(process.getName()).contains(processInstance.getName().substring(0, 2));
          }
      }
  
      @Then("the user is able to delete all process instances in query service")
      public void deleteAllProcessInstancesQuery() {
          assertThat(processQueryAdminSteps.getAllProcessInstancesAdmin()).isNotEmpty();
          processQueryAdminSteps.deleteProcessInstances();
          assertThat(processQueryAdminSteps.getAllProcessInstancesAdmin()).isEmpty();
      }
  
      @Then("the user is able to delete all events in audit service")
      public void deleteAllEventsAudit() {
          assertThat(auditAdminSteps.getEventsAdmin()).isNotEmpty();
          auditAdminSteps.deleteEvents();
          assertThat(auditAdminSteps.getEventsAdmin()).isEmpty();
      }
  }
  