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.spring.boot.process;
  
  import static org.assertj.core.api.Assertions.assertThat;
  import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
  import static org.assertj.core.api.Assertions.tuple;
  
  import java.text.ParseException;
  import java.text.SimpleDateFormat;
  import java.util.List;
  import org.activiti.api.model.shared.model.VariableInstance;
  import org.activiti.api.process.model.ProcessInstance;
  import org.activiti.api.process.model.builders.ProcessPayloadBuilder;
  import org.activiti.api.process.runtime.ProcessRuntime;
  import org.activiti.api.process.runtime.conf.ProcessRuntimeConfiguration;
  import org.activiti.api.runtime.shared.query.Page;
  import org.activiti.api.runtime.shared.query.Pageable;
  import org.activiti.api.task.model.Task;
  import org.activiti.api.task.model.builders.TaskPayloadBuilder;
  import org.activiti.api.task.runtime.TaskRuntime;
  import org.activiti.engine.ActivitiException;
  import org.activiti.spring.boot.security.util.SecurityUtil;
  import org.activiti.spring.boot.test.util.ProcessCleanUpUtil;
  import org.junit.jupiter.api.AfterEach;
  import org.junit.jupiter.api.BeforeEach;
  import org.junit.jupiter.api.Test;
  import org.springframework.beans.factory.annotation.Autowired;
  import org.springframework.boot.test.context.SpringBootTest;
  
  @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
  public class ProcessExtensionsIT {
  
      private static final String INITIAL_VARS_PROCESS = "Process_initialVarsProcess";
      private static final String INITIAL_VARS_WITH_EXPRESSIONS_PROCESS = "initialVarsExpression";
  
      @Autowired
      private ProcessRuntime processRuntime;
  
      @Autowired
      private TaskRuntime taskRuntime;
  
      @Autowired
      private SecurityUtil securityUtil;
  
      @Autowired
      private ProcessCleanUpUtil processCleanUpUtil;
  
      @AfterEach
      public void cleanUp() {
          processCleanUpUtil.cleanUpWithAdmin();
      }
  
      @BeforeEach
      void setUp() {
          securityUtil.logInAs("user");
      }
  
      @Test
      public void processInstanceHasInitialVariables() {
          ProcessRuntimeConfiguration configuration = processRuntime.configuration();
          assertThat(configuration).isNotNull();
  
          // start a process with vars then check default and specified vars exist
          ProcessInstance initialVarsProcess = processRuntime.start(
              ProcessPayloadBuilder.start()
                  .withProcessDefinitionKey(INITIAL_VARS_PROCESS)
                  .withVariable("extraVar", true)
                  .withVariable("age", 10)
                  .withBusinessKey("my business key")
                  .build()
          );
  
          assertThat(initialVarsProcess).isNotNull();
          assertThat(initialVarsProcess.getStatus()).isEqualTo(ProcessInstance.ProcessInstanceStatus.RUNNING);
  
          List<VariableInstance> variableInstances = processRuntime.variables(
              ProcessPayloadBuilder.variables().withProcessInstance(initialVarsProcess).build()
          );
  
          assertThat(variableInstances).isNotNull();
          assertThat(variableInstances).hasSize(4);
  
          assertThat(variableInstances)
              .extracting("name")
              .contains("extraVar", "name", "age", "birth")
              .doesNotContain("subscribe");
  
          // cleanup
          processRuntime.delete(ProcessPayloadBuilder.delete(initialVarsProcess));
      }
  
      @Test
      public void processInstanceHasValidInitialVariables() throws ParseException {
          ProcessRuntimeConfiguration configuration = processRuntime.configuration();
          assertThat(configuration).isNotNull();
  
          // start a process with vars then check default and specified vars exist
          ProcessInstance initialVarsProcess = processRuntime.start(
              ProcessPayloadBuilder.start()
                  .withProcessDefinitionKey(INITIAL_VARS_PROCESS)
                  .withVariable("extraVar", true)
                  .withVariable("age", 10)
                  .withVariable("name", "bob")
                  .withVariable("subscribe", true)
                  .withVariable("birth", new SimpleDateFormat("yyyy-MM-dd").parse("2009-11-30"))
                  .withBusinessKey("my business key")
                  .build()
          );
  
          assertThat(initialVarsProcess).isNotNull();
          assertThat(initialVarsProcess.getStatus()).isEqualTo(ProcessInstance.ProcessInstanceStatus.RUNNING);
  
          List<VariableInstance> variableInstances = processRuntime.variables(
              ProcessPayloadBuilder.variables().withProcessInstance(initialVarsProcess).build()
          );
  
          assertThat(variableInstances).isNotNull();
          assertThat(variableInstances).hasSize(5);
  
          assertThat(variableInstances).extracting("name").contains("extraVar", "name", "age", "birth", "subscribe");
  
          // cleanup
          processRuntime.delete(ProcessPayloadBuilder.delete(initialVarsProcess));
      }
  
      @Test
      public void processInstanceFailsWithoutRequiredVariables() {
          ProcessRuntimeConfiguration configuration = processRuntime.configuration();
          assertThat(configuration).isNotNull();
  
          assertThatExceptionOfType(ActivitiException.class)
              .isThrownBy(() -> {
                  processRuntime.start(
                      ProcessPayloadBuilder.start()
                          .withProcessDefinitionKey(INITIAL_VARS_PROCESS)
                          .withVariable("extraVar", true)
                          .build()
                  );
              })
              .withMessage("Can't start process '" + INITIAL_VARS_PROCESS + "' without required variables - age");
      }
  
      @Test
      public void processInstanceFailsIfVariableTypeIncorrect() {
          ProcessRuntimeConfiguration configuration = processRuntime.configuration();
          assertThat(configuration).isNotNull();
  
          assertThatExceptionOfType(IllegalStateException.class)
              .isThrownBy(() -> {
                  processRuntime.start(
                      ProcessPayloadBuilder.start()
                          .withProcessDefinitionKey(INITIAL_VARS_PROCESS)
                          .withVariable("age", true)
                          .withVariable("name", 7)
                          .withVariable("subscribe", "ok")
                          .withVariable("birth", "2007-10-01")
                          .build()
                  );
              })
              .withMessage("Variables fail type validation: subscribe, name, age");
      }
  
      @Test
      public void should_resolveProcessInstanceInitialVariablesExpressions_when_defined() {
          ProcessRuntimeConfiguration configuration = processRuntime.configuration();
          assertThat(configuration).isNotNull();
  
          ProcessInstance initialVarsProcess = processRuntime.start(
              ProcessPayloadBuilder.start()
                  .withProcessDefinitionKey(INITIAL_VARS_WITH_EXPRESSIONS_PROCESS)
                  .withVariable("startVariable", "startVariableValue")
                  .build()
          );
  
          assertThat(initialVarsProcess).isNotNull();
          assertThat(initialVarsProcess.getStatus()).isEqualTo(ProcessInstance.ProcessInstanceStatus.RUNNING);
  
          List<VariableInstance> variableInstances = processRuntime.variables(
              ProcessPayloadBuilder.variables().withProcessInstance(initialVarsProcess).build()
          );
  
          assertThat(variableInstances)
              .isNotNull()
              .hasSize(5)
              .extracting(VariableInstance::getName, VariableInstance::getValue)
              .containsOnly(
                  tuple("definedVar", "predefinedVarValue"),
                  tuple("startVariable", "startVariableValue"),
                  tuple(
                      "initVarWithExpressionInString",
                      "start var is startVariableValue and defined var is predefinedVarValue and undefinedVar is "
                  ),
                  tuple("initVarWithExpression", "startVariableValue"),
                  tuple("initVarWithUnresolvableExpression", null)
              );
  
          // cleanup
          processRuntime.delete(ProcessPayloadBuilder.delete(initialVarsProcess));
      }
  
      @Test
      public void should_mapProcessVariables_when_startEventMappingExists() {
          //given
          ProcessInstance process = processRuntime.start(
              ProcessPayloadBuilder.start()
                  .withProcessDefinitionKey("process-b42a166d-605b-4eec-8b96-82b1253666bf")
                  .withVariable("Text0xfems", "name_value")
                  .withVariable("Text0rvs0o", "email_value")
                  .withBusinessKey("my business key")
                  .build()
          );
  
          //when
          List<VariableInstance> variableInstances = processRuntime.variables(
              ProcessPayloadBuilder.variables().withProcessInstance(process).build()
          );
  
          //then
          assertThat(variableInstances)
              .isNotNull()
              .hasSize(2)
              .extracting(VariableInstance::getName, VariableInstance::getValue)
              .containsOnly(tuple("name", "name_value"), tuple("email", "email_value"));
  
          //when
          List<VariableInstance> taskVariables = retrieveVariablesForFirstTaskOfProcess(process);
  
          //then
          assertThat(taskVariables)
              .isNotNull()
              .hasSize(2)
              .extracting(VariableInstance::getName, VariableInstance::getValue)
              .containsOnly(tuple("nameInTask", "name_value"), tuple("emailInTask", "email_value"));
      }
  
      private List<VariableInstance> retrieveVariablesForFirstTaskOfProcess(ProcessInstance processInstance) {
          Page<Task> tasks = taskRuntime.tasks(
              Pageable.of(0, 1),
              TaskPayloadBuilder.tasksForProcess(processInstance).build()
          );
          assertThat(tasks.getContent()).hasSize(1);
          return taskRuntime.variables(
              TaskPayloadBuilder.variables().withTaskId(tasks.getContent().get(0).getId()).build()
          );
      }
  
      @Test
      public void should_mapProcessVariables_when_createAndStartProcessAreCalled() {
          //given
          ProcessInstance createdProcess = processRuntime.create(
              ProcessPayloadBuilder.create()
                  .withProcessDefinitionKey("process-b42a166d-605b-4eec-8b96-82b1253666bf")
                  .withName("TEST")
                  .build()
          );
  
          ProcessInstance startedProcess = processRuntime.startCreatedProcess(
              createdProcess.getId(),
              ProcessPayloadBuilder.start()
                  .withProcessDefinitionKey("process-b42a166d-605b-4eec-8b96-82b1253666bf")
                  .withVariable("Text0xfems", "name_value")
                  .withVariable("Text0rvs0o", "email_value")
                  .withBusinessKey("my business key")
                  .build()
          );
  
          //then
          List<VariableInstance> variableInstances = processRuntime.variables(
              ProcessPayloadBuilder.variables().withProcessInstance(startedProcess).build()
          );
  
          assertThat(variableInstances)
              .isNotNull()
              .hasSize(2)
              .extracting(VariableInstance::getName, VariableInstance::getValue)
              .containsOnly(tuple("name", "name_value"), tuple("email", "email_value"));
  
          //when
          List<VariableInstance> taskVariables = retrieveVariablesForFirstTaskOfProcess(createdProcess);
  
          //then
          assertThat(taskVariables)
              .isNotNull()
              .extracting(VariableInstance::getName, VariableInstance::getValue)
              .containsOnly(tuple("nameInTask", "name_value"), tuple("emailInTask", "email_value"));
      }
  }
  