Repository: authorjapps/zerocode
Pull request #577 (2023-05-31 15:47:26)

PR - Issue 568 typecast fix for 1D array or list
# <Feature Title>

PR Branch
**_#ADD LINK TO THE PR BRANCH_**

## Motivation and Context

## Checklist:

* [x] Unit tests added

* [x] Integration tests added

* [x] Test names are meaningful

* [x] Feature manually tested

* [x] Branch build passed

* [x] No 'package.*' in the imports

* [ ] Relevant Wiki page updated with clear instruction for the end user
  * [x] Not applicable. This was only a refactor change, no functional or behaviour changes were introduced

* [ ] Http test added to `http-testing` module(if applicable) ?
  * [x] Not applicable. The changes did not affect HTTP automation flow

* [ ] Kafka test added to `kafka-testing` module(if applicable) ?
  * [x] Not applicable. The changes did not affect Kafka automation flow



Patch:
diff --git a/core/src/main/java/org/jsmart/zerocode/core/engine/preprocessor/ZeroCodeAssertionsProcessorImpl.java b/core/src/main/java/org/jsmart/zerocode/core/engine/preprocessor/ZeroCodeAssertionsProcessorImpl.java
index 2f68c433..6eacf0c7 100644
--- a/core/src/main/java/org/jsmart/zerocode/core/engine/preprocessor/ZeroCodeAssertionsProcessorImpl.java
+++ b/core/src/main/java/org/jsmart/zerocode/core/engine/preprocessor/ZeroCodeAssertionsProcessorImpl.java
@@ -32,7 +32,7 @@ import static org.apache.commons.lang.StringEscapeUtils.escapeJava;
 import static org.apache.commons.lang.StringUtils.substringBetween;
 import static org.jsmart.zerocode.core.engine.tokens.ZeroCodeAssertionTokens.*;
 import static org.jsmart.zerocode.core.engine.tokens.ZeroCodeValueTokens.$VALUE;
-import static org.jsmart.zerocode.core.utils.FieldTypeConversionUtils.digTypeCast;
+import static org.jsmart.zerocode.core.utils.FieldTypeConversionUtils.deepTypeCast;
 import static org.jsmart.zerocode.core.utils.FieldTypeConversionUtils.fieldTypes;
 import static org.jsmart.zerocode.core.utils.PropertiesProviderUtils.loadAbsoluteProperties;
 import static org.jsmart.zerocode.core.utils.SmartUtils.isValidAbsolutePath;
@@ -392,7 +392,7 @@ public class ZeroCodeAssertionsProcessorImpl implements ZeroCodeAssertionsProces
             }
 
             Map<String, Object> fieldMap = mapper.readValue(resolvedJson, new TypeReference<Map<String, Object>>() { });
-            digTypeCast(fieldMap);
+            deepTypeCast(fieldMap);
 
             return mapper.writeValueAsString(fieldMap);
 
diff --git a/core/src/main/java/org/jsmart/zerocode/core/utils/FieldTypeConversionUtils.java b/core/src/main/java/org/jsmart/zerocode/core/utils/FieldTypeConversionUtils.java
index 3634c08a..792de27d 100755
--- a/core/src/main/java/org/jsmart/zerocode/core/utils/FieldTypeConversionUtils.java
+++ b/core/src/main/java/org/jsmart/zerocode/core/utils/FieldTypeConversionUtils.java
@@ -14,7 +14,6 @@ import org.slf4j.LoggerFactory;
 
 public class FieldTypeConversionUtils {
     private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(FieldTypeConversionUtils.class);
-    private static ObjectMapper mapper = new ObjectMapperProvider().get();
 
     public static final String INT = "(int)";
     public static final String LONG = "(long)";
@@ -49,23 +48,24 @@ public class FieldTypeConversionUtils {
         }
     };
 
-    public static void digTypeCast(Map<String, Object> map) {
+    public static void deepTypeCast(Map<String, Object> map) {
 
         map.entrySet().stream().forEach(entry -> {
 
             Object value = entry.getValue();
 
             if (value instanceof Map) {
-                digTypeCast((Map<String, Object>) value);
+                deepTypeCast((Map<String, Object>) value);
 
             } else if (value instanceof ArrayList) {
-                ((ArrayList) value).forEach(thisItem -> {
-                    if (thisItem instanceof Map) {
-                        digTypeCast((Map<String, Object>) thisItem);
+                for(int index = 0; index < ((ArrayList<?>) value).size(); index++){
+                    Object thisItemValue = ((ArrayList<?>) value).get(index);
+                    if (thisItemValue instanceof Map) {
+                        deepTypeCast((Map<String, Object>) thisItemValue);
+                    } else{
+                        replacePrimitiveValues(((ArrayList) value), index, thisItemValue);
                     }
-                    LOGGER.debug("ARRAY - Leaf node found = {}, checking for type value...", thisItem);
-                    replaceNodeValue(entry, thisItem);
-                });
+                }
             } else {
                 LOGGER.debug("Leaf node found = {}, checking for type value...", value);
                 replaceNodeValue(entry, value);
@@ -85,9 +85,24 @@ public class FieldTypeConversionUtils {
             }
         } catch (Exception exx) {
             String errorMsg = "Can not convert '" + entry.getValue() + "'.";
-            LOGGER.error(errorMsg + "\nException Details:" + exx);
+            LOGGER.error("{} with exception details: {}",  errorMsg, exx);
             throw new RuntimeException(errorMsg + exx);
         }
+    }
 
+    private static void replacePrimitiveValues(ArrayList<Object> valueList, Integer index, Object thisItem) {
+        try {
+            if (thisItem != null) {
+                fieldTypes.stream().forEach(currentType -> {
+                    if (thisItem.toString().startsWith(currentType)) {
+                        valueList.set(index, (typeMap.get(currentType)).apply(thisItem.toString()));
+                    }
+                });
+            }
+        } catch (Exception exx) {
+            String errorMsg = "Can not convert '" + thisItem + "'.";
+            LOGGER.error(errorMsg + "\nException Details:" + exx);
+            throw new RuntimeException(errorMsg + exx);
+        }
     }
 }


Test patch:
diff --git a/core/src/test/java/org/jsmart/zerocode/core/engine/preprocessor/ZeroCodeAssertionsProcessorImplTest.java b/core/src/test/java/org/jsmart/zerocode/core/engine/preprocessor/ZeroCodeAssertionsProcessorImplTest.java
index 4f2d06cc..53187875 100644
--- a/core/src/test/java/org/jsmart/zerocode/core/engine/preprocessor/ZeroCodeAssertionsProcessorImplTest.java
+++ b/core/src/test/java/org/jsmart/zerocode/core/engine/preprocessor/ZeroCodeAssertionsProcessorImplTest.java
@@ -149,6 +149,53 @@ public class ZeroCodeAssertionsProcessorImplTest {
         assertThat(resolvedSpecWithPaths, containsString("\"noOfAddresses\": \"2\""));
     }
 
+    @Test
+    public void willResolveAndTypeCast_SingleDimentionArrayElements_FromScenarioState() throws Exception {
+        String specAsString =
+                smartUtils.getJsonDocumentAsString(
+                        "unit_test_files/test_engine/02_2_resolve_typecast_in_single_dimention_arraylist_assertion.json");
+
+        final List<String> jsonPaths = jsonPreProcessor.getAllJsonPathTokens(specAsString);
+        assertThat(jsonPaths.size(), is(6));
+
+        String scenarioState =
+                "{\n"
+                        + "    \"step1\": {\n"
+                        + "        \"request\": {\n"
+                        + "            \"body\": {\n"
+                        + "                \"customer\": {\n"
+                        + "\"ids\": [\n" +
+                        "              10101,\n" +
+                        "              10102\n" +
+                        "            ],"
+                        + "                    \"firstName\": \"FIRST_NAME\",\n"
+                        + "                    \"staticName\": \"ANOTHER_NAME\",\n"
+                        + "                    \"addresses\":[\"office-1\", \"home-2\"]\n"
+                        + "                }\n"
+                        + "            }\n"
+                        + "        },\n"
+                        + "        \"response\": {\n"
+                        + "            \"id\": 10101\n"
+                        + "        }\n"
+                        + "    }\n"
+                        + "}";
+        final String resolvedSpecWithPaths =
+                jsonPreProcessor.resolveStringJson(specAsString, scenarioState);
+        System.out.println("resolvedSpecWithPaths ==> " + resolvedSpecWithPaths);
+
+        Object jsonPathValue = JsonPath.read(resolvedSpecWithPaths,
+                "$.steps[1].request.body.Customer.accounts[0]");
+
+        assertThat(jsonPathValue.getClass().getName(), is("java.lang.String"));
+
+        assertThat(resolvedSpecWithPaths, containsString("\"staticName\":\"abcde\""));
+        assertThat(resolvedSpecWithPaths, containsString("\"firstName\":\"FIRST_NAME\""));
+        assertThat(resolvedSpecWithPaths, containsString("\"firstName2\":\"FIRST_NAME\""));
+        assertThat(resolvedSpecWithPaths, containsString("\"actualName\":\"ANOTHER_NAME\""));
+        assertThat(resolvedSpecWithPaths, containsString("\"noOfAddresses\":\"2\""));
+        assertThat(resolvedSpecWithPaths, containsString("\"accounts\":[\"10101\",\"10102\"]"));
+    }
+
     @Test
     public void willResolveJsonPathOfJayWayFor_AssertionSection() throws Exception {
         ScenarioSpec scenarioSpec =
diff --git a/core/src/test/java/org/jsmart/zerocode/core/utils/FieldTypeConversionUtilsTest.java b/core/src/test/java/org/jsmart/zerocode/core/utils/FieldTypeConversionUtilsTest.java
index 2a03d1f9..adb55c8a 100644
--- a/core/src/test/java/org/jsmart/zerocode/core/utils/FieldTypeConversionUtilsTest.java
+++ b/core/src/test/java/org/jsmart/zerocode/core/utils/FieldTypeConversionUtilsTest.java
@@ -15,7 +15,7 @@ import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
 
-import static org.jsmart.zerocode.core.utils.FieldTypeConversionUtils.digTypeCast;
+import static org.jsmart.zerocode.core.utils.FieldTypeConversionUtils.deepTypeCast;
 import static org.junit.Assert.assertEquals;
 
 public class FieldTypeConversionUtilsTest {
@@ -53,6 +53,10 @@ public class FieldTypeConversionUtilsTest {
                 "      \"line1\": \"address line1\",\n" +
                 "      \"line2\": \"address line2\"\n" +
                 "    }," +
+                "   \"ids\": [\n" +
+                "              \"(int)${$.results[0].id}\",\n" +
+                "              \"(float)${$.results[1].id}\"\n" +
+                "            ]," +
                 "    \"results\": [\n" +
                 "        {\n" +
                 "            \"id\": \"(int)${$.results[0].id}\",\n" +
@@ -87,7 +91,7 @@ public class FieldTypeConversionUtilsTest {
         Map<String, Object> stepMap = mapper.readValue(resolvedJson, new TypeReference<Map<String, Object>>() {
         });
 
-        digTypeCast(stepMap);
+        deepTypeCast(stepMap);
 
         JsonNode jsonNode = mapper.valueToTree(stepMap);
 
@@ -95,6 +99,9 @@ public class FieldTypeConversionUtilsTest {
         assertEquals("{\"id\":2.35,\"name\":\"Bar - 2.35\",\"isActive\":false,\"longField\":1569683094000}",
                 jsonNode.get("results").get(1).toString());
         assertEquals("address line1", jsonNode.get("currentAddress").get("line1").asText());
+        assertEquals(jsonNode.get("ids").get(0).asInt(), jsonNode.get("results").get(0).get("id").asInt());
+        assertEquals(1, jsonNode.get("ids").get(0).asInt() );
+        assertEquals(2.35F, Float.valueOf(jsonNode.get("ids").get(1).asText()), 0);
     }
 
     @Test
@@ -158,6 +165,6 @@ public class FieldTypeConversionUtilsTest {
 
         expectedException.expectMessage("Can not convert '(int)false");
         expectedException.expect(RuntimeException.class);
-        digTypeCast(stepMap);
+        deepTypeCast(stepMap);
     }
-}
\ No newline at end of file
+}
diff --git a/core/src/test/resources/integration_test_files/type_cast/cast_types_to_int_bool_test.json b/core/src/test/resources/integration_test_files/type_cast/cast_types_to_int_bool_test.json
index e2f6d27f..21b5788a 100644
--- a/core/src/test/resources/integration_test_files/type_cast/cast_types_to_int_bool_test.json
+++ b/core/src/test/resources/integration_test_files/type_cast/cast_types_to_int_bool_test.json
@@ -40,6 +40,23 @@
                     "availability": "(boolean)${$.another_get_call.response.body.availability}"
                 }
             }
+        },
+        {
+            "name": "assert_array_elements_1D_array",
+            "url": "http://localhost:9998/home/accounts/1",
+            "operation": "GET",
+            "request": {},
+            "assertions": {
+                "status": 200,
+                "body": {
+                    "ids": [
+                        "(int)${$.another_get_call.response.body.id}"
+                    ],
+                    "name": "HBSC",
+                    "current": true
+                }
+            }
         }
+
     ]
 }
diff --git a/core/src/test/resources/simulators/test_purpose_end_points.json b/core/src/test/resources/simulators/test_purpose_end_points.json
index 0dba9e58..3971527b 100644
--- a/core/src/test/resources/simulators/test_purpose_end_points.json
+++ b/core/src/test/resources/simulators/test_purpose_end_points.json
@@ -1,6 +1,21 @@
 {
     "name": "Mock endpoints Simulator - API Stubs",
     "apis": [
+        {
+            "name": "Get Bank Account by Id",
+            "operation": "GET",
+            "url": "/home/accounts/1",
+            "response": {
+                "status": 200,
+                "body": {
+                    "ids": [
+                        1
+                    ],
+                    "name": "HBSC",
+                    "current": true
+                }
+            }
+        },
         {
             "name": "Get Bathroom by Id",
             "operation": "GET",
diff --git a/core/src/test/resources/unit_test_files/test_engine/02_2_resolve_typecast_in_single_dimention_arraylist_assertion.json b/core/src/test/resources/unit_test_files/test_engine/02_2_resolve_typecast_in_single_dimention_arraylist_assertion.json
new file mode 100644
index 00000000..224fa570
--- /dev/null
+++ b/core/src/test/resources/unit_test_files/test_engine/02_2_resolve_typecast_in_single_dimention_arraylist_assertion.json
@@ -0,0 +1,96 @@
+{
+  "scenarioName": "see assertion section",
+  "loop": 5,
+  "steps": [
+    {
+      "name": "step1",
+      "loop": 3,
+      "url": "/persons/${STATIC.ALPHABET:3}",
+      "operation": "POST",
+      "request": {
+        "body": {
+          "customer": {
+            "ids": [
+              10101,
+              10102
+            ],
+            "firstName": "FIRST_NAME",
+            "staticName": "${STATIC.ALPHABET:5}",
+            "addresses": [
+              "office-1",
+              "home-2"
+            ]
+          }
+        }
+      },
+      "assertions": {
+        "status": 201,
+        "body": {
+          "id": 1001,
+          "actualName": "ACTUAL NAME",
+          "actualNameSize": 5
+        }
+      }
+    },
+    {
+      "name": "step2",
+      "loop": 3,
+      "url": "/persons/${STATIC.ALPHABET:3}",
+      "operation": "POST",
+      "request": {
+        "body": {
+          "Customer": {
+            "id": "(int)${$.step1.request.body.customer.ids[0]}",
+            "accounts": [
+              "${$.step1.request.body.customer.ids[0]}",
+              "${$.step1.request.body.customer.ids[1]}"
+            ],
+            "firstName2": "${$.step1.request.body.customer.firstName}",
+            "nickName": "${RANDOM.NUMBER}",
+            "noOfAddresses": "${$.step1.request.body.customer.addresses.length()}"
+          }
+        }
+      },
+      "assertions": {
+        "status": 201,
+        "status": "$GT.499", //<-- cant have presence more thna once, as jackson only reads the latest value ie "$LT.199"
+        "absentField": "$GT.388",
+        //"status": "$LT.199", //<-- cant have presence more thna once, as jackson only reads the latest value ie "$LT.199"
+        "body": {
+          "id": "$NOT.NULL",
+          "salary": "$LT.1300",
+          "actualName": "${$.step1.request.body.customer.staticName}",
+          "addresses.SIZE": 5,
+          "job": {
+            "rate": 700,
+            "type": "contract"
+          },
+          "allNames": [
+            "Rose, Call me by Any Name would Smell Sweet",
+            {
+              "firstName": "R Payal",
+              "when": "Initiation",
+              "citizenship": [
+                {
+                  "country": "Italy"
+                },
+                {
+                  "country": "Noorway"
+                }
+              ],
+              "citizenship": "$[]",
+              "citizenship.SIZE": 4,
+              "personalities": "$[]",
+              "pastActivities": "$[]"
+
+            },
+            {
+              "firstName": "$CONTAINS.STRING:DaddyWithMac",
+              "when": "$NULL"
+            }
+          ]
+        }
+      }
+    }
+  ]
+}