All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.camunda.zeebe.process.test.qa.abstracts.assertions.AbstractProcessInstanceAssertTest Maven / Gradle / Ivy

There is a newer version: 8.7.0-alpha2
Show newest version
/*
 * Copyright © 2021 camunda services GmbH ([email protected])
 *
 * 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 io.camunda.zeebe.process.test.qa.abstracts.assertions;

import static org.assertj.core.api.Assertions.assertThatNoException;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import io.camunda.zeebe.client.ZeebeClient;
import io.camunda.zeebe.client.api.response.ProcessInstanceEvent;
import io.camunda.zeebe.process.test.api.ZeebeTestEngine;
import io.camunda.zeebe.process.test.assertions.BpmnAssert;
import io.camunda.zeebe.process.test.assertions.IncidentAssert;
import io.camunda.zeebe.process.test.qa.abstracts.util.Utilities;
import io.camunda.zeebe.process.test.qa.abstracts.util.Utilities.ProcessPackCallActivity;
import io.camunda.zeebe.process.test.qa.abstracts.util.Utilities.ProcessPackLoopingServiceTask;
import io.camunda.zeebe.process.test.qa.abstracts.util.Utilities.ProcessPackMessageEvent;
import io.camunda.zeebe.process.test.qa.abstracts.util.Utilities.ProcessPackMultipleCallActivity;
import io.camunda.zeebe.process.test.qa.abstracts.util.Utilities.ProcessPackMultipleTasks;
import io.camunda.zeebe.protocol.record.value.ErrorType;
import java.time.Duration;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeoutException;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;

public abstract class AbstractProcessInstanceAssertTest {

  private static final String LINE_SEPARATOR = System.lineSeparator();
  private static final Map TYPED_TEST_VARIABLES = new HashMap<>();

  static {
    TYPED_TEST_VARIABLES.put("stringProperty", "stringValue");
    TYPED_TEST_VARIABLES.put("numberProperty", 123);
    TYPED_TEST_VARIABLES.put("booleanProperty", true);
    TYPED_TEST_VARIABLES.put("complexProperty", Arrays.asList("Element 1", "Element 2"));
    TYPED_TEST_VARIABLES.put("nullProperty", null);
  }

  // These tests are for testing assertions as well as examples for users
  @Nested
  class HappyPathTests {

    private ZeebeClient client;
    private ZeebeTestEngine engine;

    @Test
    void testProcessInstanceIsStarted() throws InterruptedException, TimeoutException {
      // given
      Utilities.deployResource(client, ProcessPackLoopingServiceTask.RESOURCE_NAME);
      final Map variables =
          Collections.singletonMap(ProcessPackLoopingServiceTask.TOTAL_LOOPS, 1);

      // when
      final ProcessInstanceEvent instanceEvent =
          Utilities.startProcessInstance(
              engine, client, ProcessPackLoopingServiceTask.PROCESS_ID, variables);

      // then
      BpmnAssert.assertThat(instanceEvent).isStarted();
    }

    @Test
    void testHasCalledProcess() throws InterruptedException, TimeoutException {
      // given
      Utilities.deployResources(
          client,
          ProcessPackMultipleCallActivity.RESOURCE_NAME,
          ProcessPackMultipleCallActivity.CALLED_RESOURCE_NAME_ONE,
          ProcessPackMultipleCallActivity.CALLED_RESOURCE_NAME_TWO,
          ProcessPackMultipleCallActivity.CALLED_RESOURCE_NAME_THREE);

      // when
      final ProcessInstanceEvent instanceEvent =
          Utilities.startProcessInstance(
              engine, client, ProcessPackMultipleCallActivity.PROCESS_ID);

      // then
      BpmnAssert.assertThat(instanceEvent)
          .hasCalledProcess(ProcessPackMultipleCallActivity.CALLED_PROCESS_ID_ONE)
          .hasCalledProcess(ProcessPackMultipleCallActivity.CALLED_PROCESS_ID_TWO)
          // Specifically, the process in scope, the parent process, has not made the call to the
          // child process of the call activity
          .hasNotCalledProcess(ProcessPackMultipleCallActivity.CALLED_PROCESS_ID_THREE)
          // The child process however, has called it's own child process.
          .extractingLatestCalledProcess(ProcessPackMultipleCallActivity.CALLED_PROCESS_ID_TWO)
          .hasCalledProcess(ProcessPackMultipleCallActivity.CALLED_PROCESS_ID_THREE)
          // And the nested child process has not called any process.
          .extractingLatestCalledProcess()
          .hasNotCalledProcess();
    }

    @Test
    void testHasNotCalledProcess() throws InterruptedException, TimeoutException {
      // given
      Utilities.deployResources(
          client,
          ProcessPackCallActivity.RESOURCE_NAME,
          ProcessPackCallActivity.CALLED_RESOURCE_NAME);

      // when
      final ProcessInstanceEvent instanceEvent =
          Utilities.startProcessInstance(engine, client, ProcessPackCallActivity.PROCESS_ID);

      // then
      BpmnAssert.assertThat(instanceEvent)
          .hasCalledProcess(ProcessPackCallActivity.CALLED_PROCESS_ID)
          .hasNotCalledProcess("NonCalledProcessID")
          .hasCalledProcess()
          .extractingLatestCalledProcess()
          // Specifically, while the parent process has called a process, the called process has
          // not.
          .hasNotCalledProcess()
          .hasNotCalledProcess("NonCalledProcessID");
    }

    @Test
    void testExtractingLastCalledSpecificProcess() throws InterruptedException, TimeoutException {
      // given
      Utilities.deployResources(
          client,
          ProcessPackMultipleCallActivity.RESOURCE_NAME,
          ProcessPackMultipleCallActivity.CALLED_RESOURCE_NAME_ONE,
          ProcessPackMultipleCallActivity.CALLED_RESOURCE_NAME_TWO,
          ProcessPackMultipleCallActivity.CALLED_RESOURCE_NAME_THREE);

      // when
      final ProcessInstanceEvent instanceEvent =
          Utilities.startProcessInstance(
              engine, client, ProcessPackMultipleCallActivity.PROCESS_ID);

      // then
      // Show the correct process is extracted by checking it's called processes,
      // as we can't check processId.
      BpmnAssert.assertThat(instanceEvent)
          .extractingLatestCalledProcess(ProcessPackMultipleCallActivity.CALLED_PROCESS_ID_TWO)
          .hasCalledProcess(ProcessPackMultipleCallActivity.CALLED_PROCESS_ID_THREE);
      BpmnAssert.assertThat(instanceEvent)
          .extractingLatestCalledProcess(ProcessPackMultipleCallActivity.CALLED_PROCESS_ID_ONE)
          .hasNotCalledProcess();
    }

    @Test
    void testExtractingLastCalledProcess() throws InterruptedException, TimeoutException {
      // given
      Utilities.deployResources(
          client,
          ProcessPackMultipleCallActivity.RESOURCE_NAME,
          ProcessPackMultipleCallActivity.CALLED_RESOURCE_NAME_ONE,
          ProcessPackMultipleCallActivity.CALLED_RESOURCE_NAME_TWO,
          ProcessPackMultipleCallActivity.CALLED_RESOURCE_NAME_THREE);

      // when
      final ProcessInstanceEvent instanceEvent =
          Utilities.startProcessInstance(
              engine, client, ProcessPackMultipleCallActivity.PROCESS_ID);

      // then
      // Show the correct process is extracted by checking it's called processes,
      // as we can't check processId. The last called process has no call activities.
      BpmnAssert.assertThat(instanceEvent).extractingLatestCalledProcess().hasNotCalledProcess();
    }

    @Test
    void testProcessInstanceIsActive() throws InterruptedException, TimeoutException {
      // given
      Utilities.deployResource(client, ProcessPackLoopingServiceTask.RESOURCE_NAME);
      final Map variables =
          Collections.singletonMap(ProcessPackLoopingServiceTask.TOTAL_LOOPS, 1);

      // when
      final ProcessInstanceEvent instanceEvent =
          Utilities.startProcessInstance(
              engine, client, ProcessPackLoopingServiceTask.PROCESS_ID, variables);

      // then
      BpmnAssert.assertThat(instanceEvent).isActive();
    }

    @Test
    void testProcessInstanceIsCompleted() throws InterruptedException, TimeoutException {
      // given
      Utilities.deployResource(client, ProcessPackLoopingServiceTask.RESOURCE_NAME);
      final Map variables =
          Collections.singletonMap(ProcessPackLoopingServiceTask.TOTAL_LOOPS, 1);
      final ProcessInstanceEvent instanceEvent =
          Utilities.startProcessInstance(
              engine, client, ProcessPackLoopingServiceTask.PROCESS_ID, variables);

      // when
      Utilities.completeTask(engine, client, ProcessPackLoopingServiceTask.ELEMENT_ID);

      // then
      BpmnAssert.assertThat(instanceEvent).isCompleted();
    }

    @Test
    void testProcessInstanceIsNotCompleted() throws InterruptedException, TimeoutException {
      // given
      Utilities.deployResource(client, ProcessPackLoopingServiceTask.RESOURCE_NAME);
      final Map variables =
          Collections.singletonMap(ProcessPackLoopingServiceTask.TOTAL_LOOPS, 1);

      // when
      final ProcessInstanceEvent instanceEvent =
          Utilities.startProcessInstance(
              engine, client, ProcessPackLoopingServiceTask.PROCESS_ID, variables);

      // then
      BpmnAssert.assertThat(instanceEvent).isNotCompleted();
    }

    @Test
    void testProcessInstanceIsTerminated() throws InterruptedException, TimeoutException {
      // given
      Utilities.deployResource(client, ProcessPackLoopingServiceTask.RESOURCE_NAME);
      final Map variables =
          Collections.singletonMap(ProcessPackLoopingServiceTask.TOTAL_LOOPS, 1);
      final ProcessInstanceEvent instanceEvent =
          Utilities.startProcessInstance(
              engine, client, ProcessPackLoopingServiceTask.PROCESS_ID, variables);

      // when
      client.newCancelInstanceCommand(instanceEvent.getProcessInstanceKey()).send().join();
      Utilities.waitForIdleState(engine, Duration.ofSeconds(1));

      // then
      BpmnAssert.assertThat(instanceEvent).isTerminated();
    }

    @Test
    void testProcessInstanceIsNotTerminated() throws InterruptedException, TimeoutException {
      // given
      Utilities.deployResource(client, ProcessPackLoopingServiceTask.RESOURCE_NAME);
      final Map variables =
          Collections.singletonMap(ProcessPackLoopingServiceTask.TOTAL_LOOPS, 1);

      // when
      final ProcessInstanceEvent instanceEvent =
          Utilities.startProcessInstance(
              engine, client, ProcessPackLoopingServiceTask.PROCESS_ID, variables);

      // then
      BpmnAssert.assertThat(instanceEvent).isNotTerminated();
    }

    @Test
    void testProcessInstanceHasPassedElement() throws InterruptedException, TimeoutException {
      // given
      Utilities.deployResource(client, ProcessPackLoopingServiceTask.RESOURCE_NAME);
      final Map variables =
          Collections.singletonMap(ProcessPackLoopingServiceTask.TOTAL_LOOPS, 1);
      final ProcessInstanceEvent instanceEvent =
          Utilities.startProcessInstance(
              engine, client, ProcessPackLoopingServiceTask.PROCESS_ID, variables);

      // when
      Utilities.completeTask(engine, client, ProcessPackLoopingServiceTask.ELEMENT_ID);

      // then
      BpmnAssert.assertThat(instanceEvent)
          .hasPassedElement(ProcessPackLoopingServiceTask.ELEMENT_ID);
    }

    @Test
    void testProcessInstanceHasNotPassedElement() throws InterruptedException, TimeoutException {
      // given
      Utilities.deployResource(client, ProcessPackLoopingServiceTask.RESOURCE_NAME);
      final Map variables =
          Collections.singletonMap(ProcessPackLoopingServiceTask.TOTAL_LOOPS, 1);

      // when
      final ProcessInstanceEvent instanceEvent =
          Utilities.startProcessInstance(
              engine, client, ProcessPackLoopingServiceTask.PROCESS_ID, variables);

      // then
      BpmnAssert.assertThat(instanceEvent)
          .hasNotPassedElement(ProcessPackLoopingServiceTask.ELEMENT_ID);
    }

    @Test
    void testProcessInstanceHasPassedElementMultipleTimes()
        throws InterruptedException, TimeoutException {
      // given
      Utilities.deployResource(client, ProcessPackLoopingServiceTask.RESOURCE_NAME);
      final int totalLoops = 5;
      final Map variables =
          Collections.singletonMap(ProcessPackLoopingServiceTask.TOTAL_LOOPS, totalLoops);
      final ProcessInstanceEvent instanceEvent =
          Utilities.startProcessInstance(
              engine, client, ProcessPackLoopingServiceTask.PROCESS_ID, variables);

      // when
      for (int i = 0; i < 5; i++) {
        Utilities.completeTask(engine, client, ProcessPackLoopingServiceTask.ELEMENT_ID);
      }

      // then
      BpmnAssert.assertThat(instanceEvent)
          .hasPassedElement(ProcessPackLoopingServiceTask.ELEMENT_ID, totalLoops);
      BpmnAssert.assertThat(instanceEvent)
          .hasPassedElement(ProcessPackLoopingServiceTask.LOOP_SEQUENCE_FLOW_ID, totalLoops - 1);
    }

    @Test
    void testProcessInstanceHasPassedElementsInOrder()
        throws InterruptedException, TimeoutException {
      // given
      Utilities.deployResource(client, ProcessPackLoopingServiceTask.RESOURCE_NAME);
      final Map variables =
          Collections.singletonMap(ProcessPackLoopingServiceTask.TOTAL_LOOPS, 2);
      final ProcessInstanceEvent instanceEvent =
          Utilities.startProcessInstance(
              engine, client, ProcessPackLoopingServiceTask.PROCESS_ID, variables);

      // when
      Utilities.completeTask(engine, client, ProcessPackLoopingServiceTask.ELEMENT_ID);
      Utilities.completeTask(engine, client, ProcessPackLoopingServiceTask.ELEMENT_ID);

      // then
      BpmnAssert.assertThat(instanceEvent)
          .hasPassedElementsInOrder(
              ProcessPackLoopingServiceTask.START_EVENT_ID,
              ProcessPackLoopingServiceTask.ELEMENT_ID,
              ProcessPackLoopingServiceTask.GATEWAY_ELEMENT_ID,
              ProcessPackLoopingServiceTask.LOOP_SEQUENCE_FLOW_ID,
              ProcessPackLoopingServiceTask.ELEMENT_ID,
              ProcessPackLoopingServiceTask.GATEWAY_ELEMENT_ID,
              ProcessPackLoopingServiceTask.END_EVENT_ID);
    }

    @Test
    void testProcessInstanceIsWaitingAt() throws InterruptedException, TimeoutException {
      // given
      Utilities.deployResource(client, ProcessPackMultipleTasks.RESOURCE_NAME);

      // when
      final ProcessInstanceEvent instanceEvent =
          Utilities.startProcessInstance(engine, client, ProcessPackMultipleTasks.PROCESS_ID);

      // then
      BpmnAssert.assertThat(instanceEvent)
          .isWaitingAtElements(ProcessPackMultipleTasks.ELEMENT_ID_1);
    }

    @Test
    void testProcessIsWaitingAtMultipleElements() throws InterruptedException, TimeoutException {
      // given
      Utilities.deployResource(client, ProcessPackMultipleTasks.RESOURCE_NAME);

      // when
      final ProcessInstanceEvent instanceEvent =
          Utilities.startProcessInstance(engine, client, ProcessPackMultipleTasks.PROCESS_ID);

      // then
      BpmnAssert.assertThat(instanceEvent)
          .isWaitingAtElements(
              ProcessPackMultipleTasks.ELEMENT_ID_1,
              ProcessPackMultipleTasks.ELEMENT_ID_2,
              ProcessPackMultipleTasks.ELEMENT_ID_3);
    }

    @Test
    void testProcessInstanceIsNotWaitingAt() throws InterruptedException, TimeoutException {
      // given
      Utilities.deployResource(client, ProcessPackMultipleTasks.RESOURCE_NAME);
      final ProcessInstanceEvent instanceEvent =
          Utilities.startProcessInstance(engine, client, ProcessPackMultipleTasks.PROCESS_ID);

      // when
      Utilities.completeTask(engine, client, ProcessPackMultipleTasks.ELEMENT_ID_1);

      // then
      BpmnAssert.assertThat(instanceEvent)
          .isNotWaitingAtElements(ProcessPackMultipleTasks.ELEMENT_ID_1);
    }

    @Test
    void testProcessInstanceIsNotWaitingAtMultipleElements()
        throws InterruptedException, TimeoutException {
      // given
      Utilities.deployResource(client, ProcessPackMultipleTasks.RESOURCE_NAME);
      final ProcessInstanceEvent instanceEvent =
          Utilities.startProcessInstance(engine, client, ProcessPackMultipleTasks.PROCESS_ID);

      // when
      Utilities.completeTask(engine, client, ProcessPackMultipleTasks.ELEMENT_ID_1);
      Utilities.completeTask(engine, client, ProcessPackMultipleTasks.ELEMENT_ID_2);
      Utilities.completeTask(engine, client, ProcessPackMultipleTasks.ELEMENT_ID_3);

      // then
      BpmnAssert.assertThat(instanceEvent)
          .isNotWaitingAtElements(
              ProcessPackMultipleTasks.ELEMENT_ID_1,
              ProcessPackMultipleTasks.ELEMENT_ID_2,
              ProcessPackMultipleTasks.ELEMENT_ID_3);
    }

    @Test
    void testProcessInstanceIsNotWaitingAtNonExistingElement()
        throws InterruptedException, TimeoutException {
      // given
      Utilities.deployResource(client, ProcessPackMultipleTasks.RESOURCE_NAME);

      // when
      final ProcessInstanceEvent instanceEvent =
          Utilities.startProcessInstance(engine, client, ProcessPackMultipleTasks.PROCESS_ID);
      final String nonExistingElementId = "non-existing-task";

      // then
      BpmnAssert.assertThat(instanceEvent).isNotWaitingAtElements(nonExistingElementId);
    }

    @Test
    void testProcessInstanceIsWaitingExactlyAtElements()
        throws InterruptedException, TimeoutException {
      // given
      Utilities.deployResource(client, ProcessPackMultipleTasks.RESOURCE_NAME);
      final ProcessInstanceEvent instanceEvent =
          Utilities.startProcessInstance(engine, client, ProcessPackMultipleTasks.PROCESS_ID);

      // when
      Utilities.completeTask(engine, client, ProcessPackMultipleTasks.ELEMENT_ID_1);

      // then
      BpmnAssert.assertThat(instanceEvent)
          .isWaitingExactlyAtElements(
              ProcessPackMultipleTasks.ELEMENT_ID_2, ProcessPackMultipleTasks.ELEMENT_ID_3);
    }

    @Test
    void testProcessInstanceIsWaitingForMessage() throws InterruptedException, TimeoutException {
      // given
      Utilities.deployResource(client, ProcessPackMessageEvent.RESOURCE_NAME);
      final Map variables =
          Collections.singletonMap(ProcessPackMessageEvent.CORRELATION_KEY_VARIABLE, "key");

      // when
      final ProcessInstanceEvent instanceEvent =
          Utilities.startProcessInstance(
              engine, client, ProcessPackMessageEvent.PROCESS_ID, variables);

      // then
      BpmnAssert.assertThat(instanceEvent)
          .isWaitingForMessages(ProcessPackMessageEvent.MESSAGE_NAME);
    }

    @Test
    void testProcessInstanceIsNotWaitingForMessage() throws InterruptedException, TimeoutException {
      // given
      Utilities.deployResource(client, ProcessPackMessageEvent.RESOURCE_NAME);
      final String correlationKey = "key";
      final Map variables =
          Collections.singletonMap(
              ProcessPackMessageEvent.CORRELATION_KEY_VARIABLE, correlationKey);
      final ProcessInstanceEvent instanceEvent =
          Utilities.startProcessInstance(
              engine, client, ProcessPackMessageEvent.PROCESS_ID, variables);

      // when
      Utilities.sendMessage(engine, client, ProcessPackMessageEvent.MESSAGE_NAME, correlationKey);

      // then
      BpmnAssert.assertThat(instanceEvent)
          .isNotWaitingForMessages(ProcessPackMessageEvent.MESSAGE_NAME);
    }

    @Test
    void testProcessInstanceHasVariable() throws InterruptedException, TimeoutException {
      // given
      Utilities.deployResource(client, ProcessPackLoopingServiceTask.RESOURCE_NAME);
      final Map variables =
          Collections.singletonMap(ProcessPackLoopingServiceTask.TOTAL_LOOPS, 1);

      // when
      final ProcessInstanceEvent instanceEvent =
          Utilities.startProcessInstance(
              engine, client, ProcessPackLoopingServiceTask.PROCESS_ID, variables);

      // then
      BpmnAssert.assertThat(instanceEvent).hasVariable(ProcessPackLoopingServiceTask.TOTAL_LOOPS);
    }

    @Test
    void testProcessInstanceHasVariableWithValue() throws InterruptedException, TimeoutException {
      // given
      Utilities.deployResource(client, ProcessPackLoopingServiceTask.RESOURCE_NAME);

      // when
      final ProcessInstanceEvent instanceEvent =
          Utilities.startProcessInstance(
              engine, client, ProcessPackLoopingServiceTask.PROCESS_ID, TYPED_TEST_VARIABLES);

      // then
      assertThatNoException()
          .isThrownBy(
              () ->
                  TYPED_TEST_VARIABLES.forEach(
                      (key, value) ->
                          BpmnAssert.assertThat(instanceEvent).hasVariableWithValue(key, value)));
    }

    @Test
    void testHasCorrelatedMessageByName() throws InterruptedException, TimeoutException {
      // given
      Utilities.deployResource(client, ProcessPackMessageEvent.RESOURCE_NAME);
      final String correlationKey = "key";
      final Map variables =
          Collections.singletonMap("correlationKey", correlationKey);
      final ProcessInstanceEvent instanceEvent =
          Utilities.startProcessInstance(
              engine, client, ProcessPackMessageEvent.PROCESS_ID, variables);

      // when
      Utilities.sendMessage(engine, client, ProcessPackMessageEvent.MESSAGE_NAME, correlationKey);

      // then
      BpmnAssert.assertThat(instanceEvent)
          .hasCorrelatedMessageByName(ProcessPackMessageEvent.MESSAGE_NAME, 1);
    }

    @Test
    void testHasCorrelatedMessageByCorrelationKey() throws InterruptedException, TimeoutException {
      // given
      Utilities.deployResource(client, ProcessPackMessageEvent.RESOURCE_NAME);
      final String correlationKey = "key";
      final Map variables =
          Collections.singletonMap(
              ProcessPackMessageEvent.CORRELATION_KEY_VARIABLE, correlationKey);
      final ProcessInstanceEvent instanceEvent =
          Utilities.startProcessInstance(
              engine, client, ProcessPackMessageEvent.PROCESS_ID, variables);

      // when
      Utilities.sendMessage(engine, client, ProcessPackMessageEvent.MESSAGE_NAME, correlationKey);

      // then
      BpmnAssert.assertThat(instanceEvent).hasCorrelatedMessageByCorrelationKey(correlationKey, 1);
    }

    @Test
    void testHasAnyIncidents() throws InterruptedException, TimeoutException {
      // given
      Utilities.deployResource(client, ProcessPackLoopingServiceTask.RESOURCE_NAME);

      final Map variables =
          Collections.singletonMap(
              ProcessPackLoopingServiceTask.TOTAL_LOOPS, "invalid value"); // will cause incident

      // when
      final ProcessInstanceEvent instanceEvent =
          Utilities.startProcessInstance(
              engine, client, ProcessPackLoopingServiceTask.PROCESS_ID, variables);
      /* will raise an incident in the gateway because ProcessPackLoopingServiceTask.TOTAL_LOOPS is a string, but needs to be an int */
      Utilities.completeTask(engine, client, ProcessPackLoopingServiceTask.ELEMENT_ID);

      // then
      BpmnAssert.assertThat(instanceEvent).hasAnyIncidents();
    }

    @Test
    void testHasNoIncidents() throws InterruptedException, TimeoutException {
      // given
      Utilities.deployResource(client, ProcessPackLoopingServiceTask.RESOURCE_NAME);

      // when
      final ProcessInstanceEvent instanceEvent =
          Utilities.startProcessInstance(engine, client, ProcessPackLoopingServiceTask.PROCESS_ID);

      // then
      BpmnAssert.assertThat(instanceEvent).hasNoIncidents();
    }

    @Test
    void testExtractLatestIncident() throws InterruptedException, TimeoutException {
      // given
      Utilities.deployResource(client, ProcessPackLoopingServiceTask.RESOURCE_NAME);

      final Map variables =
          Collections.singletonMap(
              ProcessPackLoopingServiceTask.TOTAL_LOOPS, "invalid value"); // will cause incident

      // when
      final ProcessInstanceEvent instanceEvent =
          Utilities.startProcessInstance(
              engine, client, ProcessPackLoopingServiceTask.PROCESS_ID, variables);
      /* will raise an incident in the gateway because ProcessPackLoopingServiceTask.TOTAL_LOOPS is a string, but needs to be an int */
      Utilities.completeTask(engine, client, ProcessPackLoopingServiceTask.ELEMENT_ID);

      final IncidentAssert incidentAssert =
          BpmnAssert.assertThat(instanceEvent).extractingLatestIncident();

      // then

      Assertions.assertThat(incidentAssert).isNotNull();
      incidentAssert
          .isUnresolved()
          .hasErrorType(ErrorType.EXTRACT_VALUE_ERROR)
          .wasRaisedInProcessInstance(instanceEvent);
    }
  }

  // These tests are just for assertion testing purposes. These should not be used as examples.
  @Nested
  class UnhappyPathTests {

    private ZeebeClient client;
    private ZeebeTestEngine engine;

    @Test
    void testProcessInstanceIsStartedFailure() throws InterruptedException, TimeoutException {
      // given
      Utilities.deployResource(client, ProcessPackLoopingServiceTask.RESOURCE_NAME);
      final ProcessInstanceEvent mockInstanceEvent = mock(ProcessInstanceEvent.class);

      // when
      when(mockInstanceEvent.getProcessInstanceKey()).thenReturn(-1L);

      // then
      org.junit.jupiter.api.Assertions.assertThrows(
          AssertionError.class,
          BpmnAssert.assertThat(mockInstanceEvent)::isStarted,
          "Process with key -1 was not started");
    }

    @Test
    void testProcessInstanceIsNotStartedIfProcessInstanceKeyNoMatch()
        throws InterruptedException, TimeoutException {
      // given
      Utilities.deployResource(client, ProcessPackLoopingServiceTask.RESOURCE_NAME);
      Utilities.startProcessInstance(engine, client, ProcessPackLoopingServiceTask.PROCESS_ID);
      final ProcessInstanceEvent mockInstanceEvent = mock(ProcessInstanceEvent.class);

      // when
      when(mockInstanceEvent.getProcessInstanceKey()).thenReturn(-1L);

      // then
      assertThatThrownBy(() -> BpmnAssert.assertThat(mockInstanceEvent).isStarted())
          .isInstanceOf(AssertionError.class)
          .hasMessage("Process with key -1 was not started");
    }

    @Test
    void testProcessInstanceIsActiveFailure() throws InterruptedException, TimeoutException {
      // given
      Utilities.deployResource(client, ProcessPackLoopingServiceTask.RESOURCE_NAME);
      final ProcessInstanceEvent instanceEvent =
          Utilities.startProcessInstance(
              engine,
              client,
              ProcessPackLoopingServiceTask.PROCESS_ID,
              Collections.singletonMap(ProcessPackLoopingServiceTask.TOTAL_LOOPS, 1));

      // when
      Utilities.completeTask(engine, client, ProcessPackLoopingServiceTask.ELEMENT_ID);

      // then
      assertThatThrownBy(() -> BpmnAssert.assertThat(instanceEvent).isActive())
          .isInstanceOf(AssertionError.class)
          .hasMessage("Process with key %s is not active", instanceEvent.getProcessInstanceKey());
    }

    @Test
    void testProcessInstanceIsCompletedFailure() throws InterruptedException, TimeoutException {
      // given
      Utilities.deployResource(client, ProcessPackLoopingServiceTask.RESOURCE_NAME);

      // when
      final ProcessInstanceEvent instanceEvent =
          Utilities.startProcessInstance(
              engine,
              client,
              ProcessPackLoopingServiceTask.PROCESS_ID,
              Collections.singletonMap(ProcessPackLoopingServiceTask.TOTAL_LOOPS, 1));

      // then
      assertThatThrownBy(() -> BpmnAssert.assertThat(instanceEvent).isCompleted())
          .isInstanceOf(AssertionError.class)
          .hasMessage(
              "Process with key %s was not completed", instanceEvent.getProcessInstanceKey());
    }

    @Test
    void testProcessInstanceIsNotCompletedFailure() throws InterruptedException, TimeoutException {
      // given
      Utilities.deployResource(client, ProcessPackLoopingServiceTask.RESOURCE_NAME);
      final ProcessInstanceEvent instanceEvent =
          Utilities.startProcessInstance(
              engine,
              client,
              ProcessPackLoopingServiceTask.PROCESS_ID,
              Collections.singletonMap(ProcessPackLoopingServiceTask.TOTAL_LOOPS, 1));

      // when
      Utilities.completeTask(engine, client, ProcessPackLoopingServiceTask.ELEMENT_ID);

      // then
      assertThatThrownBy(() -> BpmnAssert.assertThat(instanceEvent).isNotCompleted())
          .isInstanceOf(AssertionError.class)
          .hasMessage("Process with key %s was completed", instanceEvent.getProcessInstanceKey());
    }

    @Test
    void testProcessInstanceIsTerminatedFailure() throws InterruptedException, TimeoutException {
      // given
      Utilities.deployResource(client, ProcessPackLoopingServiceTask.RESOURCE_NAME);

      // when
      final ProcessInstanceEvent instanceEvent =
          Utilities.startProcessInstance(
              engine,
              client,
              ProcessPackLoopingServiceTask.PROCESS_ID,
              Collections.singletonMap(ProcessPackLoopingServiceTask.TOTAL_LOOPS, 1));

      // then
      assertThatThrownBy(() -> BpmnAssert.assertThat(instanceEvent).isTerminated())
          .isInstanceOf(AssertionError.class)
          .hasMessage(
              "Process with key %s was not terminated", instanceEvent.getProcessInstanceKey());
    }

    @Test
    void testHasCalledProcessFailNoProcessCalled() throws InterruptedException, TimeoutException {
      // given
      Utilities.deployResources(
          client,
          ProcessPackCallActivity.RESOURCE_NAME,
          ProcessPackCallActivity.CALLED_RESOURCE_NAME);

      // when
      final ProcessInstanceEvent instanceEvent =
          Utilities.startProcessInstance(engine, client, ProcessPackCallActivity.PROCESS_ID);

      // then
      assertThatThrownBy(
              () ->
                  BpmnAssert.assertThat(instanceEvent)
                      .extractingLatestCalledProcess()
                      .hasCalledProcess())
          .isInstanceOf(AssertionError.class)
          .hasMessage("No process was called from this process");
    }

    @Test
    void testHasCalledProcessFailSpecificProcessCalled()
        throws InterruptedException, TimeoutException {
      // given
      Utilities.deployResources(
          client,
          ProcessPackCallActivity.RESOURCE_NAME,
          ProcessPackCallActivity.CALLED_RESOURCE_NAME);

      // when
      final ProcessInstanceEvent instanceEvent =
          Utilities.startProcessInstance(engine, client, ProcessPackCallActivity.PROCESS_ID);

      // then
      assertThatThrownBy(
              () -> BpmnAssert.assertThat(instanceEvent).hasCalledProcess("NonCalledProcessID"))
          .isInstanceOf(AssertionError.class)
          .hasMessage("No process with id `NonCalledProcessID` was called from this process");
    }

    @Test
    void testHasNotCalledProcessFailSpecificProcess()
        throws InterruptedException, TimeoutException {
      // given
      Utilities.deployResources(
          client,
          ProcessPackCallActivity.RESOURCE_NAME,
          ProcessPackCallActivity.CALLED_RESOURCE_NAME);

      // when
      final ProcessInstanceEvent instanceEvent =
          Utilities.startProcessInstance(engine, client, ProcessPackCallActivity.PROCESS_ID);

      // then
      assertThatThrownBy(
              () ->
                  BpmnAssert.assertThat(instanceEvent)
                      .hasNotCalledProcess(ProcessPackCallActivity.CALLED_PROCESS_ID))
          .isInstanceOf(AssertionError.class)
          .hasMessage(
              "A process with id `%s` was called from this process",
              ProcessPackCallActivity.CALLED_PROCESS_ID);
    }

    @Test
    void testHasNotCalledProcessFailAnyProcess() throws InterruptedException, TimeoutException {
      // given
      Utilities.deployResources(
          client,
          ProcessPackCallActivity.RESOURCE_NAME,
          ProcessPackCallActivity.CALLED_RESOURCE_NAME);

      // when
      final ProcessInstanceEvent instanceEvent =
          Utilities.startProcessInstance(engine, client, ProcessPackCallActivity.PROCESS_ID);

      // then
      assertThatThrownBy(() -> BpmnAssert.assertThat(instanceEvent).hasNotCalledProcess())
          .isInstanceOf(AssertionError.class)
          .hasMessage(
              "A process was called from this process, distinct called processes are: [start-end]");
    }

    @Test
    void testHasNotCalledProcessFailMultipleProcesses()
        throws InterruptedException, TimeoutException {
      // given
      Utilities.deployResources(
          client,
          ProcessPackMultipleCallActivity.RESOURCE_NAME,
          ProcessPackMultipleCallActivity.CALLED_RESOURCE_NAME_ONE,
          ProcessPackMultipleCallActivity.CALLED_RESOURCE_NAME_TWO,
          ProcessPackMultipleCallActivity.CALLED_RESOURCE_NAME_THREE);

      // when
      final ProcessInstanceEvent instanceEvent =
          Utilities.startProcessInstance(
              engine, client, ProcessPackMultipleCallActivity.PROCESS_ID);

      // then
      assertThatThrownBy(() -> BpmnAssert.assertThat(instanceEvent).hasNotCalledProcess())
          .isInstanceOf(AssertionError.class)
          .hasMessage(
              "A process was called from this process, distinct called processes are: [alternate-start-end, call-activity]");
    }

    @Test
    void testExtractingLatestCalledProcessFailAnyProcess()
        throws InterruptedException, TimeoutException {
      // given
      Utilities.deployResources(
          client,
          ProcessPackCallActivity.RESOURCE_NAME,
          ProcessPackCallActivity.CALLED_RESOURCE_NAME);

      // when
      final ProcessInstanceEvent instanceEvent =
          Utilities.startProcessInstance(engine, client, ProcessPackCallActivity.PROCESS_ID);

      // then
      assertThatThrownBy(
              () ->
                  BpmnAssert.assertThat(instanceEvent)
                      // We use a process that has a call activity, to make sure the child process
                      // doesn't
                      // extract itself because of a reference to the parent process.
                      .extractingLatestCalledProcess()
                      .extractingLatestCalledProcess())
          .isInstanceOf(AssertionError.class)
          .hasMessage("No process was called from this process");
    }

    @Test
    void testExtractingLatestCalledProcessFailSpecificProcess()
        throws InterruptedException, TimeoutException {
      // given
      Utilities.deployResources(
          client,
          ProcessPackCallActivity.RESOURCE_NAME,
          ProcessPackCallActivity.CALLED_RESOURCE_NAME);

      // when
      final ProcessInstanceEvent instanceEvent =
          Utilities.startProcessInstance(engine, client, ProcessPackCallActivity.PROCESS_ID);

      // then
      assertThatThrownBy(
              () ->
                  BpmnAssert.assertThat(instanceEvent)
                      .extractingLatestCalledProcess("NonCalledProcessId"))
          .isInstanceOf(AssertionError.class)
          .hasMessage("No process with id `NonCalledProcessId` was called from this process");
    }

    @Test
    void testProcessInstanceIsNotTerminatedFailure() throws InterruptedException, TimeoutException {
      // given
      Utilities.deployResource(client, ProcessPackLoopingServiceTask.RESOURCE_NAME);
      final ProcessInstanceEvent instanceEvent =
          Utilities.startProcessInstance(
              engine,
              client,
              ProcessPackLoopingServiceTask.PROCESS_ID,
              Collections.singletonMap(ProcessPackLoopingServiceTask.TOTAL_LOOPS, 1));

      // when
      client.newCancelInstanceCommand(instanceEvent.getProcessInstanceKey()).send().join();
      Utilities.waitForIdleState(engine, Duration.ofSeconds(1));

      // then
      assertThatThrownBy(() -> BpmnAssert.assertThat(instanceEvent).isNotTerminated())
          .isInstanceOf(AssertionError.class)
          .hasMessage("Process with key %s was terminated", instanceEvent.getProcessInstanceKey());
    }

    @Test
    void testProcessInstanceHasPassedElementFailure()
        throws InterruptedException, TimeoutException {
      // given
      Utilities.deployResource(client, ProcessPackLoopingServiceTask.RESOURCE_NAME);

      // when
      final ProcessInstanceEvent instanceEvent =
          Utilities.startProcessInstance(
              engine,
              client,
              ProcessPackLoopingServiceTask.PROCESS_ID,
              Collections.singletonMap(ProcessPackLoopingServiceTask.TOTAL_LOOPS, 1));

      // then
      assertThatThrownBy(
              () ->
                  BpmnAssert.assertThat(instanceEvent)
                      .hasPassedElement(ProcessPackLoopingServiceTask.ELEMENT_ID))
          .isInstanceOf(AssertionError.class)
          .hasMessage(
              "Expected element with id %s to be passed 1 times, but was 0",
              ProcessPackLoopingServiceTask.ELEMENT_ID);
    }

    @Test
    void testProcessInstanceHasNotPassedElementFailure()
        throws InterruptedException, TimeoutException {
      // given
      Utilities.deployResource(client, ProcessPackLoopingServiceTask.RESOURCE_NAME);
      final ProcessInstanceEvent instanceEvent =
          Utilities.startProcessInstance(
              engine,
              client,
              ProcessPackLoopingServiceTask.PROCESS_ID,
              Collections.singletonMap(ProcessPackLoopingServiceTask.TOTAL_LOOPS, 1));

      // when
      Utilities.completeTask(engine, client, ProcessPackLoopingServiceTask.ELEMENT_ID);

      // then
      assertThatThrownBy(
              () ->
                  BpmnAssert.assertThat(instanceEvent)
                      .hasNotPassedElement(ProcessPackLoopingServiceTask.ELEMENT_ID))
          .isInstanceOf(AssertionError.class)
          .hasMessage(
              "Expected element with id %s to be passed 0 times, but was 1",
              ProcessPackLoopingServiceTask.ELEMENT_ID);
    }

    @Test
    void testProcessInstanceHasPassedElementsInOrderFailure()
        throws InterruptedException, TimeoutException {
      // given
      Utilities.deployResource(client, ProcessPackLoopingServiceTask.RESOURCE_NAME);
      final Map variables =
          Collections.singletonMap(ProcessPackLoopingServiceTask.TOTAL_LOOPS, 1);
      final ProcessInstanceEvent instanceEvent =
          Utilities.startProcessInstance(
              engine, client, ProcessPackLoopingServiceTask.PROCESS_ID, variables);

      // when
      Utilities.completeTask(engine, client, ProcessPackLoopingServiceTask.ELEMENT_ID);

      // then
      assertThatThrownBy(
              () ->
                  BpmnAssert.assertThat(instanceEvent)
                      .hasPassedElementsInOrder(
                          ProcessPackLoopingServiceTask.END_EVENT_ID,
                          ProcessPackLoopingServiceTask.ELEMENT_ID,
                          ProcessPackLoopingServiceTask.START_EVENT_ID))
          .isInstanceOf(AssertionError.class)
          .hasMessage(
              "[Ordered elements] "
                  + LINE_SEPARATOR
                  + "expected: [\"endevent\", \"servicetask\", \"startevent\"]"
                  + LINE_SEPARATOR
                  + " but was: [\"startevent\", \"servicetask\", \"endevent\"]");
    }

    @Test
    void testProcessInstanceIsWaitingAtFailure() throws InterruptedException, TimeoutException {
      // given
      Utilities.deployResource(client, ProcessPackMultipleTasks.RESOURCE_NAME);
      final ProcessInstanceEvent instanceEvent =
          Utilities.startProcessInstance(engine, client, ProcessPackMultipleTasks.PROCESS_ID);

      // when
      Utilities.completeTask(engine, client, ProcessPackMultipleTasks.ELEMENT_ID_1);

      // then
      assertThatThrownBy(
              () ->
                  BpmnAssert.assertThat(instanceEvent)
                      .isWaitingAtElements(ProcessPackMultipleTasks.ELEMENT_ID_1))
          .isInstanceOf(AssertionError.class)
          .hasMessageContainingAll("to contain", ProcessPackMultipleTasks.ELEMENT_ID_1);
    }

    @Test
    void testProcessInstanceIsWaitingAtMultipleElementsFailure()
        throws InterruptedException, TimeoutException {
      // given
      Utilities.deployResource(client, ProcessPackMultipleTasks.RESOURCE_NAME);
      final ProcessInstanceEvent instanceEvent =
          Utilities.startProcessInstance(engine, client, ProcessPackMultipleTasks.PROCESS_ID);

      // when
      Utilities.completeTask(engine, client, ProcessPackMultipleTasks.ELEMENT_ID_1);
      Utilities.completeTask(engine, client, ProcessPackMultipleTasks.ELEMENT_ID_2);
      Utilities.completeTask(engine, client, ProcessPackMultipleTasks.ELEMENT_ID_3);

      // then
      assertThatThrownBy(
              () ->
                  BpmnAssert.assertThat(instanceEvent)
                      .isWaitingAtElements(
                          ProcessPackMultipleTasks.ELEMENT_ID_1,
                          ProcessPackMultipleTasks.ELEMENT_ID_2,
                          ProcessPackMultipleTasks.ELEMENT_ID_3))
          .isInstanceOf(AssertionError.class)
          .hasMessageContainingAll(
              "to contain:",
              ProcessPackMultipleTasks.ELEMENT_ID_1,
              ProcessPackMultipleTasks.ELEMENT_ID_2,
              ProcessPackMultipleTasks.ELEMENT_ID_3);
    }

    @Test
    void testProcessInstanceWaitingAtNonExistingElementFailure()
        throws InterruptedException, TimeoutException {
      // given
      Utilities.deployResource(client, ProcessPackMultipleTasks.RESOURCE_NAME);

      // when
      final ProcessInstanceEvent instanceEvent =
          Utilities.startProcessInstance(engine, client, ProcessPackMultipleTasks.PROCESS_ID);
      final String nonExistingTaskId = "non-existing-task";

      // then
      assertThatThrownBy(
              () -> BpmnAssert.assertThat(instanceEvent).isWaitingAtElements(nonExistingTaskId))
          .isInstanceOf(AssertionError.class)
          .hasMessageContainingAll("to contain", nonExistingTaskId);
    }

    @Test
    void testProcessInstanceIsNotWaitingAtFailure() throws InterruptedException, TimeoutException {
      // given
      Utilities.deployResource(client, ProcessPackMultipleTasks.RESOURCE_NAME);

      // when
      final ProcessInstanceEvent instanceEvent =
          Utilities.startProcessInstance(engine, client, ProcessPackMultipleTasks.PROCESS_ID);

      // then
      assertThatThrownBy(
              () ->
                  BpmnAssert.assertThat(instanceEvent)
                      .isNotWaitingAtElements(ProcessPackMultipleTasks.ELEMENT_ID_1))
          .isInstanceOf(AssertionError.class)
          .hasMessageContainingAll("not to contain", ProcessPackMultipleTasks.ELEMENT_ID_1);
    }

    @Test
    void testProcessInstanceIsNotWaitingAtMulitpleElementsFailure()
        throws InterruptedException, TimeoutException {
      // given
      Utilities.deployResource(client, ProcessPackMultipleTasks.RESOURCE_NAME);

      // when
      final ProcessInstanceEvent instanceEvent =
          Utilities.startProcessInstance(engine, client, ProcessPackMultipleTasks.PROCESS_ID);

      // then
      assertThatThrownBy(
              () ->
                  BpmnAssert.assertThat(instanceEvent)
                      .isNotWaitingAtElements(
                          ProcessPackMultipleTasks.ELEMENT_ID_1,
                          ProcessPackMultipleTasks.ELEMENT_ID_2,
                          ProcessPackMultipleTasks.ELEMENT_ID_3))
          .isInstanceOf(AssertionError.class)
          .hasMessageContainingAll(
              "not to contain",
              ProcessPackMultipleTasks.ELEMENT_ID_1,
              ProcessPackMultipleTasks.ELEMENT_ID_2,
              ProcessPackMultipleTasks.ELEMENT_ID_3);
    }

    @Test
    void testProcessInstanceIsWaitingExactlyAtElementsFailure_tooManyElements()
        throws InterruptedException, TimeoutException {
      // given
      Utilities.deployResource(client, ProcessPackMultipleTasks.RESOURCE_NAME);

      // when
      final ProcessInstanceEvent instanceEvent =
          Utilities.startProcessInstance(engine, client, ProcessPackMultipleTasks.PROCESS_ID);

      // then
      assertThatThrownBy(
              () ->
                  BpmnAssert.assertThat(instanceEvent)
                      .isWaitingExactlyAtElements(ProcessPackMultipleTasks.ELEMENT_ID_1))
          .isInstanceOf(AssertionError.class)
          .hasMessageContainingAll(
              String.format(
                  "Process with key %s is waiting at element(s) with id(s)",
                  instanceEvent.getProcessInstanceKey()),
              ProcessPackMultipleTasks.ELEMENT_ID_2,
              ProcessPackMultipleTasks.ELEMENT_ID_3)
          .hasMessageNotContaining(ProcessPackMultipleTasks.ELEMENT_ID_1);
    }

    @Test
    void testProcessInstanceIsWaitingExactlyAtElementsFailure_tooLittleElements()
        throws InterruptedException, TimeoutException {
      // given
      Utilities.deployResource(client, ProcessPackMultipleTasks.RESOURCE_NAME);

      // when
      final ProcessInstanceEvent instanceEvent =
          Utilities.startProcessInstance(engine, client, ProcessPackMultipleTasks.PROCESS_ID);
      Utilities.completeTask(engine, client, ProcessPackMultipleTasks.ELEMENT_ID_1);
      Utilities.completeTask(engine, client, ProcessPackMultipleTasks.ELEMENT_ID_2);

      // then
      assertThatThrownBy(
              () ->
                  BpmnAssert.assertThat(instanceEvent)
                      .isWaitingExactlyAtElements(
                          ProcessPackMultipleTasks.ELEMENT_ID_1,
                          ProcessPackMultipleTasks.ELEMENT_ID_2,
                          ProcessPackMultipleTasks.ELEMENT_ID_3))
          .isInstanceOf(AssertionError.class)
          .hasMessageContainingAll(
              String.format(
                  "Process with key %s is not waiting at element(s) with id(s)",
                  instanceEvent.getProcessInstanceKey()),
              ProcessPackMultipleTasks.ELEMENT_ID_1,
              ProcessPackMultipleTasks.ELEMENT_ID_2)
          .hasMessageNotContaining(ProcessPackMultipleTasks.ELEMENT_ID_3);
    }

    @Test
    void testProcessInstanceIsWaitingExactlyAtElementsFailure_combination()
        throws InterruptedException, TimeoutException {
      // given
      Utilities.deployResource(client, ProcessPackMultipleTasks.RESOURCE_NAME);

      // when
      final ProcessInstanceEvent instanceEvent =
          Utilities.startProcessInstance(engine, client, ProcessPackMultipleTasks.PROCESS_ID);
      Utilities.completeTask(engine, client, ProcessPackMultipleTasks.ELEMENT_ID_1);
      Utilities.completeTask(engine, client, ProcessPackMultipleTasks.ELEMENT_ID_2);

      // then
      assertThatThrownBy(
              () ->
                  BpmnAssert.assertThat(instanceEvent)
                      .isWaitingExactlyAtElements(
                          ProcessPackMultipleTasks.ELEMENT_ID_1,
                          ProcessPackMultipleTasks.ELEMENT_ID_2))
          .isInstanceOf(AssertionError.class)
          .hasMessageContainingAll(
              String.format(
                  "Process with key %s is not waiting at element(s) with id(s)",
                  instanceEvent.getProcessInstanceKey()),
              ProcessPackMultipleTasks.ELEMENT_ID_1,
              ProcessPackMultipleTasks.ELEMENT_ID_2,
              String.format(
                  "Process with key %s is waiting at element(s) with id(s)",
                  instanceEvent.getProcessInstanceKey()),
              ProcessPackMultipleTasks.ELEMENT_ID_3);
    }

    @Test
    void testProcessInstanceIsWaitingForMessageFailure()
        throws InterruptedException, TimeoutException {
      // given
      Utilities.deployResource(client, ProcessPackMessageEvent.RESOURCE_NAME);
      final String correlationKey = "key";
      final Map variables =
          Collections.singletonMap("correlationKey", correlationKey);
      final ProcessInstanceEvent instanceEvent =
          Utilities.startProcessInstance(
              engine, client, ProcessPackMessageEvent.PROCESS_ID, variables);

      // when
      Utilities.sendMessage(engine, client, ProcessPackMessageEvent.MESSAGE_NAME, correlationKey);

      // then
      assertThatThrownBy(
              () ->
                  BpmnAssert.assertThat(instanceEvent)
                      .isWaitingForMessages(ProcessPackMessageEvent.MESSAGE_NAME))
          .isInstanceOf(AssertionError.class)
          .hasMessageContainingAll("to contain:", ProcessPackMessageEvent.MESSAGE_NAME);
    }

    @Test
    void testProcessInstanceIsNotWaitingForMessageFailure()
        throws InterruptedException, TimeoutException {
      // given
      Utilities.deployResource(client, ProcessPackMessageEvent.RESOURCE_NAME);
      final String correlationKey = "key";
      final Map variables =
          Collections.singletonMap("correlationKey", correlationKey);

      // when
      final ProcessInstanceEvent instanceEvent =
          Utilities.startProcessInstance(
              engine, client, ProcessPackMessageEvent.PROCESS_ID, variables);

      // then
      assertThatThrownBy(
              () ->
                  BpmnAssert.assertThat(instanceEvent)
                      .isNotWaitingForMessages(ProcessPackMessageEvent.MESSAGE_NAME))
          .isInstanceOf(AssertionError.class)
          .hasMessageContainingAll("not to contain", ProcessPackMessageEvent.MESSAGE_NAME);
    }

    @Test
    void testProcessInstanceHasVariableFailure() throws InterruptedException, TimeoutException {
      // given
      Utilities.deployResource(client, ProcessPackLoopingServiceTask.RESOURCE_NAME);
      final String expectedVariable = "variable";
      final String actualVariable = "loopAmount";

      // when
      final ProcessInstanceEvent instanceEvent =
          Utilities.startProcessInstance(engine, client, ProcessPackLoopingServiceTask.PROCESS_ID);

      // then
      assertThatThrownBy(() -> BpmnAssert.assertThat(instanceEvent).hasVariable(expectedVariable))
          .isInstanceOf(AssertionError.class)
          .hasMessage(
              "Unable to find variable with name `%s`. Available variables are: [%s]",
              expectedVariable, actualVariable);
    }

    @Test
    void testProcessInstanceHasVariableWithValueFailure()
        throws InterruptedException, TimeoutException {
      // given
      Utilities.deployResource(client, ProcessPackLoopingServiceTask.RESOURCE_NAME);
      final String variable = "variable";
      final String expectedValue = "expectedValue";
      final String actualValue = "actualValue";
      final Map variables = Collections.singletonMap(variable, actualValue);

      // when
      final ProcessInstanceEvent instanceEvent =
          Utilities.startProcessInstance(
              engine, client, ProcessPackLoopingServiceTask.PROCESS_ID, variables);

      // then
      assertThatThrownBy(
              () ->
                  BpmnAssert.assertThat(instanceEvent)
                      .hasVariableWithValue(variable, expectedValue))
          .isInstanceOf(AssertionError.class)
          .hasMessage(
              "The variable '%s' does not have the expected value. The value passed in"
                  + " ('%s') is internally mapped to a JSON String that yields '\"%s\"'. However, the "
                  + "actual value (as JSON String) is '\"%s\"'.",
              variable, expectedValue, expectedValue, actualValue);
    }

    @Test
    void testHasCorrelatedMessageByNameFailure() throws InterruptedException, TimeoutException {
      // given
      Utilities.deployResource(client, ProcessPackMessageEvent.RESOURCE_NAME);
      final String correlationKey = "key";
      final Map variables =
          Collections.singletonMap("correlationKey", correlationKey);

      // when
      final ProcessInstanceEvent instanceEvent =
          Utilities.startProcessInstance(
              engine, client, ProcessPackMessageEvent.PROCESS_ID, variables);

      // then
      assertThatThrownBy(
              () ->
                  BpmnAssert.assertThat(instanceEvent)
                      .hasCorrelatedMessageByName(ProcessPackMessageEvent.MESSAGE_NAME, 1))
          .isInstanceOf(AssertionError.class)
          .hasMessage(
              "Expected message with name '%s' to be correlated %d times, but was %d times",
              ProcessPackMessageEvent.MESSAGE_NAME, 1, 0);
    }

    @Test
    void testHasCorrelatedMessageByCorrelationKeyFailure()
        throws InterruptedException, TimeoutException {
      // given
      Utilities.deployResource(client, ProcessPackMessageEvent.RESOURCE_NAME);
      final String correlationKey = "key";
      final Map variables =
          Collections.singletonMap("correlationKey", correlationKey);

      // when
      final ProcessInstanceEvent instanceEvent =
          Utilities.startProcessInstance(
              engine, client, ProcessPackMessageEvent.PROCESS_ID, variables);

      // then
      assertThatThrownBy(
              () ->
                  BpmnAssert.assertThat(instanceEvent)
                      .hasCorrelatedMessageByCorrelationKey(correlationKey, 1))
          .isInstanceOf(AssertionError.class)
          .hasMessage(
              "Expected message with correlation key '%s' to be correlated %d "
                  + "times, but was %d times",
              correlationKey, 1, 0);
    }

    @Test
    void testHasAnyIncidentsFailure() throws InterruptedException, TimeoutException {
      // given
      Utilities.deployResource(client, ProcessPackLoopingServiceTask.RESOURCE_NAME);

      // when
      final ProcessInstanceEvent instanceEvent =
          Utilities.startProcessInstance(engine, client, ProcessPackLoopingServiceTask.PROCESS_ID);

      // then
      assertThatThrownBy(() -> BpmnAssert.assertThat(instanceEvent).hasAnyIncidents())
          .isInstanceOf(AssertionError.class)
          .hasMessage("No incidents were raised for this process instance");
    }

    @Test
    void testHasNoIncidentsFailure() throws InterruptedException, TimeoutException {
      // given
      Utilities.deployResource(client, ProcessPackLoopingServiceTask.RESOURCE_NAME);
      final Map variables =
          Collections.singletonMap(
              ProcessPackLoopingServiceTask.TOTAL_LOOPS, "invalid value"); // will cause incident

      // when
      final ProcessInstanceEvent instanceEvent =
          Utilities.startProcessInstance(
              engine, client, ProcessPackLoopingServiceTask.PROCESS_ID, variables);
      /* will raise an incident in the gateway because ProcessPackLoopingServiceTask.TOTAL_LOOPS is a string, but needs to be an int */
      Utilities.completeTask(engine, client, ProcessPackLoopingServiceTask.ELEMENT_ID);

      // then
      assertThatThrownBy(() -> BpmnAssert.assertThat(instanceEvent).hasNoIncidents())
          .isInstanceOf(AssertionError.class)
          .hasMessage("Incidents were raised for this process instance");
    }

    @Test
    void testExtractLatestIncidentFailure() throws InterruptedException, TimeoutException {
      // given
      Utilities.deployResource(client, ProcessPackLoopingServiceTask.RESOURCE_NAME);

      // when
      final ProcessInstanceEvent instanceEvent =
          Utilities.startProcessInstance(engine, client, ProcessPackLoopingServiceTask.PROCESS_ID);

      // then
      assertThatThrownBy(() -> BpmnAssert.assertThat(instanceEvent).extractingLatestIncident())
          .isInstanceOf(AssertionError.class)
          .hasMessage("No incidents were raised for this process instance");
    }
  }

  // These tests validate bug fixes for bugs that have occurred in the past
  @Nested
  class RegressionTests {

    private ZeebeClient client;
    private ZeebeTestEngine engine;

    @Test // regression test for #78
    public void testShouldCaptureLatestValueOfVariable()
        throws InterruptedException, TimeoutException {
      // given
      Utilities.deployResource(client, ProcessPackLoopingServiceTask.RESOURCE_NAME);
      final Map variables1 =
          Collections.singletonMap(ProcessPackLoopingServiceTask.TOTAL_LOOPS, "1");

      final Map variables2 =
          Collections.singletonMap(ProcessPackLoopingServiceTask.TOTAL_LOOPS, "2");

      final Map variables3 =
          Collections.singletonMap(ProcessPackLoopingServiceTask.TOTAL_LOOPS, "3");

      // when
      final ProcessInstanceEvent instanceEvent =
          Utilities.startProcessInstance(
              engine, client, ProcessPackLoopingServiceTask.PROCESS_ID, variables1);

      client
          .newSetVariablesCommand(instanceEvent.getProcessInstanceKey())
          .variables(variables2)
          .send()
          .join();
      client
          .newSetVariablesCommand(instanceEvent.getProcessInstanceKey())
          .variables(variables3)
          .send()
          .join();

      Utilities.waitForIdleState(engine, Duration.ofSeconds(1));

      // then
      BpmnAssert.assertThat(instanceEvent)
          .hasVariableWithValue(ProcessPackLoopingServiceTask.TOTAL_LOOPS, "3");
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy