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 java.util.Arrays.asList;
  import static org.assertj.core.api.Assertions.assertThat;
  import static org.assertj.core.api.Assertions.catchThrowable;
  import static org.assertj.core.api.Assertions.tuple;
  
  import com.fasterxml.jackson.databind.ObjectMapper;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  import org.activiti.api.model.shared.model.VariableInstance;
  import org.activiti.api.process.model.ProcessInstance;
  import org.activiti.engine.ActivitiException;
  import org.activiti.spring.boot.test.util.ProcessCleanUpUtil;
  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;
  import org.springframework.test.context.TestPropertySource;
  
  @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
  @TestPropertySource(locations = { "classpath:application.properties" })
  public class ProcessRuntimeVariableMappingTest {
  
      private static final String VARIABLE_MAPPING_PROCESS = "connectorVarMapping";
      private static final String VARIABLE_MAPPING_EXPRESSION_PROCESS = "connectorVarMappingExpression";
      private static final String OUTPUT_MAPPING_EXPRESSION_VARIABLE_PROCESS = "outputMappingExpVar";
      private static final String OUTPUT_MAPPING_EXPRESSION_VALUE_PROCESS = "outputMappingExpValue";
  
      @Autowired
      private ProcessBaseRuntime processBaseRuntime;
  
      @Autowired
      private ObjectMapper mapper;
  
      @Autowired
      private ProcessCleanUpUtil processCleanUpUtil;
  
      @BeforeEach
      public void setUp() {
          processCleanUpUtil.cleanUpWithAdmin();
      }
  
      @Test
      public void shouldMapVariables() {
          ProcessInstance processInstance = processBaseRuntime.startProcessWithProcessDefinitionKey(
              VARIABLE_MAPPING_PROCESS
          );
  
          List<VariableInstance> variables = processBaseRuntime.getProcessVariablesByProcessId(processInstance.getId());
  
          assertThat(variables)
              .extracting(VariableInstance::getName, VariableInstance::getValue)
              .containsOnly(
                  tuple("name", "outName"),
                  tuple("age", 35),
                  tuple("input_unmapped_variable_with_matching_name", "inTest"),
                  tuple("input_unmapped_variable_with_non_matching_connector_input_name", "inTest"),
                  tuple("nickName", "testName"),
                  tuple("out_unmapped_variable_matching_name", "default"),
                  tuple("output_unmapped_variable_with_non_matching_connector_output_name", "default")
              );
      }
  
      @Test
      public void should_resolveExpression_when_expressionIsInInputMappingValueOrInMappedProperty() {
          ProcessInstance processInstance = processBaseRuntime.startProcessWithProcessDefinitionKey(
              VARIABLE_MAPPING_EXPRESSION_PROCESS
          );
  
          List<VariableInstance> variables = processBaseRuntime.getProcessVariablesByProcessId(processInstance.getId());
  
          String[] array = { "first", "John", "Doe", "last" };
          List<String> list = asList(array);
  
          Map<String, Object> data = new HashMap<>();
          data.put("age-in-months", 240);
          data.put("full-name", "John Doe");
          data.put("demoString", "expressionResolved");
          data.put("list", list);
  
          assertThat(variables)
              .extracting(VariableInstance::getName, VariableInstance::getValue)
              .containsOnly(
                  tuple("age", 30),
                  tuple("name", "outName"),
                  tuple("surname", "Doe"),
                  tuple("data", data),
                  tuple("user-msg", "Hello John Doe, today is your 20th birthday! It means 7305.0 days of life"),
                  tuple("input-unmapped-variable-with-matching-name", "Doe"),
                  tuple("input-unmapped-variable-with-non-matching-connector-input-name", "inTestExpression"),
                  tuple("variableToResolve", "John"),
                  tuple("out-unmapped-variable-matching-name", "defaultExpression"),
                  tuple("output-unmapped-variable-with-non-matching-connector-output-name", "defaultExpression"),
                  tuple("resident", true)
              );
      }
  
      @Test
      public void should_throwActivitiException_when_expressionIsInOutputMapping() {
          Throwable throwable = catchThrowable(() ->
              processBaseRuntime.startProcessWithProcessDefinitionKey(OUTPUT_MAPPING_EXPRESSION_VARIABLE_PROCESS)
          );
  
          assertThat(throwable)
              .isInstanceOf(ActivitiException.class)
              .hasMessageContaining("Expressions are not allowed as variable values in the output mapping");
      }
  
      @Test
      public void should_resolveExpression_when_expressionIsInOutputMappingValueOrInMappedProperty() {
          ProcessInstance processInstance = processBaseRuntime.startProcessWithProcessDefinitionKey(
              OUTPUT_MAPPING_EXPRESSION_VALUE_PROCESS
          );
  
          List<VariableInstance> variables = processBaseRuntime.getProcessVariablesByProcessId(processInstance.getId());
  
          assertThat(variables)
              .extracting(VariableInstance::getName, VariableInstance::getValue)
              .containsOnly(
                  tuple("name", "John"),
                  tuple("outVar", "Resolved expression: value-set-in-connector"),
                  tuple("outVarFromJsonExpression", "Tower of London")
              );
      }
  }
  