This is an automated email from the ASF dual-hosted git repository.

hansva pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/hop.git


The following commit(s) were added to refs/heads/main by this push:
     new 25643ea641 fix #5759, allow empty parameters in http client (#5762)
25643ea641 is described below

commit 25643ea641930fb7636f2bbc5e2529221d9740a3
Author: Hans Van Akelyen <[email protected]>
AuthorDate: Thu Oct 2 15:51:44 2025 +0200

    fix #5759, allow empty parameters in http client (#5762)
---
 ... => 0002-http-client-test-empty-parameters.hpl} |  74 ++-
 .../http/0002-http-client-test-parameters.hpl      |  49 +-
 integration-tests/http/main-0002-http-client.hwf   | 145 +++--
 .../apache/hop/pipeline/transforms/http/Http.java  |  17 +-
 .../hop/pipeline/transforms/http/HttpData.java     |   2 +-
 .../hop/pipeline/transforms/http/HttpMeta.java     | 282 +--------
 .../hop/pipeline/transforms/http/HttpDataTest.java | 255 ++++++++
 .../hop/pipeline/transforms/http/HttpMetaTest.java | 341 +++++++++++
 .../hop/pipeline/transforms/http/HttpTest.java     | 661 +++++++++++++++++++--
 9 files changed, 1358 insertions(+), 468 deletions(-)

diff --git a/integration-tests/http/0002-http-client-test-parameters.hpl 
b/integration-tests/http/0002-http-client-test-empty-parameters.hpl
similarity index 88%
copy from integration-tests/http/0002-http-client-test-parameters.hpl
copy to integration-tests/http/0002-http-client-test-empty-parameters.hpl
index 861aa5902e..181f13721c 100644
--- a/integration-tests/http/0002-http-client-test-parameters.hpl
+++ b/integration-tests/http/0002-http-client-test-empty-parameters.hpl
@@ -19,7 +19,7 @@ limitations under the License.
 -->
 <pipeline>
   <info>
-    <name>0002-http-client-test-parameters</name>
+    <name>0002-http-client-test-empty-parameters</name>
     <name_sync_with_filename>Y</name_sync_with_filename>
     <description/>
     <extended_description/>
@@ -40,8 +40,6 @@ limitations under the License.
     <created_date>2021/09/27 10:47:43.922</created_date>
     <modified_user>-</modified_user>
     <modified_date>2021/09/27 10:47:43.922</modified_date>
-    <key_for_session_key>H4sIAAAAAAAAAAMAAAAAAAAAAAA=</key_for_session_key>
-    <is_key_private>N</is_key_private>
   </info>
   <notepads>
   </notepads>
@@ -102,16 +100,30 @@ limitations under the License.
       <field>
         <length>-1</length>
         <name>parameter</name>
+        <nullif>parametervalue</nullif>
+        <precision>-1</precision>
+        <set_empty_string>N</set_empty_string>
+        <type>String</type>
+      </field>
+      <field>
+        <length>-1</length>
+        <name>empty_string</name>
+        <precision>-1</precision>
+        <set_empty_string>Y</set_empty_string>
+        <type>String</type>
+      </field>
+      <field>
+        <length>-1</length>
+        <name>null_string</name>
         <precision>-1</precision>
         <set_empty_string>N</set_empty_string>
         <type>String</type>
-        <nullif>parametervalue</nullif>
       </field>
     </fields>
     <interval_in_ms>5000</interval_in_ms>
     <last_time_field>FiveSecondsAgo</last_time_field>
-    <never_ending>N</never_ending>
     <limit>1</limit>
+    <never_ending>N</never_ending>
     <row_time_field>now</row_time_field>
     <attributes/>
     <GUI>
@@ -132,6 +144,7 @@ limitations under the License.
     </partitioning>
     <url>http://${HOSTNAME}/get</url>
     <urlInField>N</urlInField>
+    <ignoreSsl>N</ignoreSsl>
     <urlField/>
     <encoding>UTF-8</encoding>
     <httpLogin/>
@@ -146,6 +159,14 @@ limitations under the License.
         <name>parameter</name>
         <parameter>parameter</parameter>
       </arg>
+      <arg>
+        <name>empty_string</name>
+        <parameter>empty_parameter</parameter>
+      </arg>
+      <arg>
+        <name>null_string</name>
+        <parameter>null_parameter</parameter>
+      </arg>
     </lookup>
     <result>
       <name>result</name>
@@ -232,44 +253,47 @@ limitations under the License.
       <method>none</method>
       <schema_name/>
     </partitioning>
-    <send_true_to/>
-    <send_false_to/>
     <compare>
       <condition>
-        <negated>N</negated>
         <conditions>
           <condition>
-            <negated>Y</negated>
-            <leftvalue>result_status</leftvalue>
+            <conditions>
+</conditions>
             <function>=</function>
-            <rightvalue/>
+            <leftvalue>result_status</leftvalue>
+            <negated>Y</negated>
+            <operator>-</operator>
             <value>
-              <name>constant</name>
-              <type>Integer</type>
-              <text>200</text>
-              <length>-1</length>
-              <precision>0</precision>
               <isnull>N</isnull>
+              <length>-1</length>
               <mask>#</mask>
+              <name>constant</name>
+              <precision>0</precision>
+              <text>200</text>
+              <type>Integer</type>
             </value>
           </condition>
           <condition>
-            <negated>Y</negated>
-            <operator>AND</operator>
-            <leftvalue>parameter_result</leftvalue>
+            <conditions>
+</conditions>
             <function>=</function>
-            <rightvalue/>
+            <leftvalue>parameter_result</leftvalue>
+            <negated>Y</negated>
+            <operator>OR</operator>
             <value>
-              <name>constant</name>
-              <type>String</type>
-              <text>{"parameter":"parametervalue"}</text>
-              <length>-1</length>
-              <precision>-1</precision>
               <isnull>N</isnull>
+              <length>-1</length>
               <mask/>
+              <name>constant</name>
+              <precision>-1</precision>
+              
<text>{"empty_parameter":"","null_parameter":"","parameter":"parametervalue"}</text>
+              <type>String</type>
             </value>
           </condition>
         </conditions>
+        <function>=</function>
+        <negated>N</negated>
+        <operator>-</operator>
       </condition>
     </compare>
     <attributes/>
diff --git a/integration-tests/http/0002-http-client-test-parameters.hpl 
b/integration-tests/http/0002-http-client-test-parameters.hpl
index 861aa5902e..7cada798ef 100644
--- a/integration-tests/http/0002-http-client-test-parameters.hpl
+++ b/integration-tests/http/0002-http-client-test-parameters.hpl
@@ -40,8 +40,6 @@ limitations under the License.
     <created_date>2021/09/27 10:47:43.922</created_date>
     <modified_user>-</modified_user>
     <modified_date>2021/09/27 10:47:43.922</modified_date>
-    <key_for_session_key>H4sIAAAAAAAAAAMAAAAAAAAAAAA=</key_for_session_key>
-    <is_key_private>N</is_key_private>
   </info>
   <notepads>
   </notepads>
@@ -102,16 +100,16 @@ limitations under the License.
       <field>
         <length>-1</length>
         <name>parameter</name>
+        <nullif>parametervalue</nullif>
         <precision>-1</precision>
         <set_empty_string>N</set_empty_string>
         <type>String</type>
-        <nullif>parametervalue</nullif>
       </field>
     </fields>
     <interval_in_ms>5000</interval_in_ms>
     <last_time_field>FiveSecondsAgo</last_time_field>
-    <never_ending>N</never_ending>
     <limit>1</limit>
+    <never_ending>N</never_ending>
     <row_time_field>now</row_time_field>
     <attributes/>
     <GUI>
@@ -132,6 +130,7 @@ limitations under the License.
     </partitioning>
     <url>http://${HOSTNAME}/get</url>
     <urlInField>N</urlInField>
+    <ignoreSsl>N</ignoreSsl>
     <urlField/>
     <encoding>UTF-8</encoding>
     <httpLogin/>
@@ -232,44 +231,46 @@ limitations under the License.
       <method>none</method>
       <schema_name/>
     </partitioning>
-    <send_true_to/>
-    <send_false_to/>
     <compare>
       <condition>
-        <negated>N</negated>
         <conditions>
           <condition>
-            <negated>Y</negated>
-            <leftvalue>result_status</leftvalue>
+            <conditions>
+</conditions>
             <function>=</function>
-            <rightvalue/>
+            <leftvalue>result_status</leftvalue>
+            <negated>Y</negated>
+            <operator>-</operator>
             <value>
-              <name>constant</name>
-              <type>Integer</type>
-              <text>200</text>
-              <length>-1</length>
-              <precision>0</precision>
               <isnull>N</isnull>
+              <length>-1</length>
               <mask>#</mask>
+              <name>constant</name>
+              <precision>0</precision>
+              <text>200</text>
+              <type>Integer</type>
             </value>
           </condition>
           <condition>
-            <negated>Y</negated>
-            <operator>AND</operator>
-            <leftvalue>parameter_result</leftvalue>
+            <conditions>
+</conditions>
             <function>=</function>
-            <rightvalue/>
+            <leftvalue>parameter_result</leftvalue>
+            <negated>Y</negated>
+            <operator>OR</operator>
             <value>
-              <name>constant</name>
-              <type>String</type>
-              <text>{"parameter":"parametervalue"}</text>
+              <isnull>N</isnull>
               <length>-1</length>
+              <name>constant</name>
               <precision>-1</precision>
-              <isnull>N</isnull>
-              <mask/>
+              <text>{"parameter":"parametervalue"}</text>
+              <type>String</type>
             </value>
           </condition>
         </conditions>
+        <function>=</function>
+        <negated>N</negated>
+        <operator>-</operator>
       </condition>
     </compare>
     <attributes/>
diff --git a/integration-tests/http/main-0002-http-client.hwf 
b/integration-tests/http/main-0002-http-client.hwf
index d266f856d7..95b630c318 100644
--- a/integration-tests/http/main-0002-http-client.hwf
+++ b/integration-tests/http/main-0002-http-client.hwf
@@ -41,14 +41,15 @@ limitations under the License.
       <description/>
       <type>SPECIAL</type>
       <attributes/>
-      <repeat>N</repeat>
-      <schedulerType>0</schedulerType>
-      <intervalSeconds>0</intervalSeconds>
-      <intervalMinutes>60</intervalMinutes>
+      <DayOfMonth>1</DayOfMonth>
+      <doNotWaitOnFirstExecution>N</doNotWaitOnFirstExecution>
       <hour>12</hour>
+      <intervalMinutes>60</intervalMinutes>
+      <intervalSeconds>0</intervalSeconds>
       <minutes>0</minutes>
+      <repeat>N</repeat>
+      <schedulerType>0</schedulerType>
       <weekDay>1</weekDay>
-      <DayOfMonth>1</DayOfMonth>
       <parallel>N</parallel>
       <xloc>50</xloc>
       <yloc>48</yloc>
@@ -59,74 +60,56 @@ limitations under the License.
       <description/>
       <type>PIPELINE</type>
       <attributes/>
-      <filename>${PROJECT_HOME}/0002-http-client.hpl</filename>
-      <params_from_previous>N</params_from_previous>
-      <exec_per_row>N</exec_per_row>
-      <clear_rows>N</clear_rows>
-      <clear_files>N</clear_files>
-      <set_logfile>N</set_logfile>
-      <logfile/>
-      <logext/>
       <add_date>N</add_date>
       <add_time>N</add_time>
-      <loglevel>Basic</loglevel>
-      <set_append_logfile>N</set_append_logfile>
-      <wait_until_finished>Y</wait_until_finished>
-      <follow_abort_remote>N</follow_abort_remote>
+      <clear_files>N</clear_files>
+      <clear_rows>N</clear_rows>
       <create_parent_folder>N</create_parent_folder>
-      <run_configuration>local</run_configuration>
+      <exec_per_row>N</exec_per_row>
+      <filename>${PROJECT_HOME}/0002-http-client.hpl</filename>
+      <loglevel>Basic</loglevel>
       <parameters>
-        <pass_all_parameters>Y</pass_all_parameters>
         <parameter>
           <name>HOSTNAME</name>
-          <stream_name/>
           <value>${HOSTNAME}</value>
         </parameter>
+        <pass_all_parameters>Y</pass_all_parameters>
       </parameters>
+      <params_from_previous>N</params_from_previous>
+      <run_configuration>local</run_configuration>
+      <set_append_logfile>N</set_append_logfile>
+      <set_logfile>N</set_logfile>
+      <wait_until_finished>Y</wait_until_finished>
       <parallel>N</parallel>
       <xloc>310</xloc>
       <yloc>48</yloc>
       <attributes_hac/>
     </action>
-    <action>
-      <name>Abort workflow</name>
-      <description/>
-      <type>ABORT</type>
-      <attributes/>
-      <parallel>N</parallel>
-      <xloc>570</xloc>
-      <yloc>208</yloc>
-      <attributes_hac/>
-    </action>
     <action>
       <name>0002-http-client-test-headers.hpl</name>
       <description/>
       <type>PIPELINE</type>
       <attributes/>
-      <filename>${PROJECT_HOME}/0002-http-client-test-headers.hpl</filename>
-      <params_from_previous>N</params_from_previous>
-      <exec_per_row>N</exec_per_row>
-      <clear_rows>N</clear_rows>
-      <clear_files>N</clear_files>
-      <set_logfile>N</set_logfile>
-      <logfile/>
-      <logext/>
       <add_date>N</add_date>
       <add_time>N</add_time>
-      <loglevel>Basic</loglevel>
-      <set_append_logfile>N</set_append_logfile>
-      <wait_until_finished>Y</wait_until_finished>
-      <follow_abort_remote>N</follow_abort_remote>
+      <clear_files>N</clear_files>
+      <clear_rows>N</clear_rows>
       <create_parent_folder>N</create_parent_folder>
-      <run_configuration>local</run_configuration>
+      <exec_per_row>N</exec_per_row>
+      <filename>${PROJECT_HOME}/0002-http-client-test-headers.hpl</filename>
+      <loglevel>Basic</loglevel>
       <parameters>
-        <pass_all_parameters>Y</pass_all_parameters>
         <parameter>
           <name>HOSTNAME</name>
-          <stream_name/>
           <value>${HOSTNAME}</value>
         </parameter>
+        <pass_all_parameters>Y</pass_all_parameters>
       </parameters>
+      <params_from_previous>N</params_from_previous>
+      <run_configuration>local</run_configuration>
+      <set_append_logfile>N</set_append_logfile>
+      <set_logfile>N</set_logfile>
+      <wait_until_finished>Y</wait_until_finished>
       <parallel>N</parallel>
       <xloc>570</xloc>
       <yloc>48</yloc>
@@ -137,35 +120,59 @@ limitations under the License.
       <description/>
       <type>PIPELINE</type>
       <attributes/>
-      <filename>${PROJECT_HOME}/0002-http-client-test-parameters.hpl</filename>
-      <params_from_previous>N</params_from_previous>
-      <exec_per_row>N</exec_per_row>
-      <clear_rows>N</clear_rows>
-      <clear_files>N</clear_files>
-      <set_logfile>N</set_logfile>
-      <logfile/>
-      <logext/>
       <add_date>N</add_date>
       <add_time>N</add_time>
-      <loglevel>Basic</loglevel>
-      <set_append_logfile>N</set_append_logfile>
-      <wait_until_finished>Y</wait_until_finished>
-      <follow_abort_remote>N</follow_abort_remote>
+      <clear_files>N</clear_files>
+      <clear_rows>N</clear_rows>
       <create_parent_folder>N</create_parent_folder>
-      <run_configuration>local</run_configuration>
+      <exec_per_row>N</exec_per_row>
+      <filename>${PROJECT_HOME}/0002-http-client-test-parameters.hpl</filename>
+      <loglevel>Basic</loglevel>
       <parameters>
-        <pass_all_parameters>Y</pass_all_parameters>
         <parameter>
           <name>HOSTNAME</name>
-          <stream_name/>
           <value>${HOSTNAME}</value>
         </parameter>
+        <pass_all_parameters>Y</pass_all_parameters>
       </parameters>
+      <params_from_previous>N</params_from_previous>
+      <run_configuration>local</run_configuration>
+      <set_append_logfile>N</set_append_logfile>
+      <set_logfile>N</set_logfile>
+      <wait_until_finished>Y</wait_until_finished>
       <parallel>N</parallel>
       <xloc>830</xloc>
       <yloc>48</yloc>
       <attributes_hac/>
     </action>
+    <action>
+      <name>0002-http-client-test-empty-parameters.hpl</name>
+      <description/>
+      <type>PIPELINE</type>
+      <attributes/>
+      <add_date>N</add_date>
+      <add_time>N</add_time>
+      <clear_files>N</clear_files>
+      <clear_rows>N</clear_rows>
+      <create_parent_folder>N</create_parent_folder>
+      <exec_per_row>N</exec_per_row>
+      
<filename>${PROJECT_HOME}/0002-http-client-test-empty-parameters.hpl</filename>
+      <logext/>
+      <logfile/>
+      <loglevel>Basic</loglevel>
+      <parameters>
+        <pass_all_parameters>Y</pass_all_parameters>
+      </parameters>
+      <params_from_previous>N</params_from_previous>
+      <run_configuration>local</run_configuration>
+      <set_append_logfile>N</set_append_logfile>
+      <set_logfile>N</set_logfile>
+      <wait_until_finished>Y</wait_until_finished>
+      <parallel>N</parallel>
+      <xloc>1120</xloc>
+      <yloc>48</yloc>
+      <attributes_hac/>
+    </action>
   </actions>
   <hops>
     <hop>
@@ -175,13 +182,6 @@ limitations under the License.
       <evaluation>Y</evaluation>
       <unconditional>Y</unconditional>
     </hop>
-    <hop>
-      <from>0002-http-client.hpl</from>
-      <to>Abort workflow</to>
-      <enabled>Y</enabled>
-      <evaluation>N</evaluation>
-      <unconditional>N</unconditional>
-    </hop>
     <hop>
       <from>0002-http-client.hpl</from>
       <to>0002-http-client-test-headers.hpl</to>
@@ -196,18 +196,11 @@ limitations under the License.
       <evaluation>Y</evaluation>
       <unconditional>N</unconditional>
     </hop>
-    <hop>
-      <from>0002-http-client-test-headers.hpl</from>
-      <to>Abort workflow</to>
-      <enabled>Y</enabled>
-      <evaluation>N</evaluation>
-      <unconditional>N</unconditional>
-    </hop>
     <hop>
       <from>0002-http-client-test-parameters.hpl</from>
-      <to>Abort workflow</to>
+      <to>0002-http-client-test-empty-parameters.hpl</to>
       <enabled>Y</enabled>
-      <evaluation>N</evaluation>
+      <evaluation>Y</evaluation>
       <unconditional>N</unconditional>
     </hop>
   </hops>
diff --git 
a/plugins/transforms/http/src/main/java/org/apache/hop/pipeline/transforms/http/Http.java
 
b/plugins/transforms/http/src/main/java/org/apache/hop/pipeline/transforms/http/Http.java
index 95a702a0f7..18e37f9e2c 100644
--- 
a/plugins/transforms/http/src/main/java/org/apache/hop/pipeline/transforms/http/Http.java
+++ 
b/plugins/transforms/http/src/main/java/org/apache/hop/pipeline/transforms/http/Http.java
@@ -126,17 +126,17 @@ public class Http extends BaseTransform<HttpMeta, 
HttpData> {
 
       // Add Custom Http headers
       if (data.useHeaderParameters) {
-        for (int i = 0; i < data.header_parameters_nrs.length; i++) {
+        for (int i = 0; i < data.headerParametersNrs.length; i++) {
           method.addHeader(
               data.headerParameters[i].getName(),
-              data.inputRowMeta.getString(rowData, 
data.header_parameters_nrs[i]));
+              data.inputRowMeta.getString(rowData, 
data.headerParametersNrs[i]));
           if (isDebug()) {
             logDebug(
                 BaseMessages.getString(
                     PKG,
                     "HTTPDialog.Log.HeaderValue",
                     data.headerParameters[i].getName(),
-                    data.inputRowMeta.getString(rowData, 
data.header_parameters_nrs[i])));
+                    data.inputRowMeta.getString(rowData, 
data.headerParametersNrs[i])));
           }
         }
       }
@@ -271,13 +271,12 @@ public class Http extends BaseTransform<HttpMeta, 
HttpData> {
       }
 
       uriBuilder = new URIBuilder(baseUrl); // the base URL with variable 
substitution
-      List<NameValuePair> queryParams = uriBuilder.getQueryParams();
 
       for (int i = 0; i < data.argnrs.length; i++) {
         String key = meta.getArgumentParameter()[i];
         String value = outputRowMeta.getString(row, data.argnrs[i]);
-        if (!key.isEmpty() && !value.isEmpty()) {
-          uriBuilder.addParameter(key, value);
+        if (!key.isEmpty()) {
+          uriBuilder.addParameter(key, Const.NVL(value, ""));
         }
       }
     } catch (Exception e) {
@@ -337,7 +336,7 @@ public class Http extends BaseTransform<HttpMeta, HttpData> 
{
         data.useHeaderParameters = true;
       }
 
-      data.header_parameters_nrs = new int[nrHeaders];
+      data.headerParametersNrs = new int[nrHeaders];
       data.headerParameters = new NameValuePair[nrHeaders];
 
       // get the headers
@@ -353,11 +352,11 @@ public class Http extends BaseTransform<HttpMeta, 
HttpData> {
                   PKG, CONST_HTTP_EXCEPTION_ERROR_FINDING_FIELD, 
meta.getHeaderField()[i]));
         }
 
-        data.header_parameters_nrs[i] = fieldIndex;
+        data.headerParametersNrs[i] = fieldIndex;
         data.headerParameters[i] =
             new BasicNameValuePair(
                 resolve(meta.getHeaderParameter()[i]),
-                data.outputRowMeta.getString(r, 
data.header_parameters_nrs[i]));
+                data.outputRowMeta.getString(r, data.headerParametersNrs[i]));
       }
     } // end if first
 
diff --git 
a/plugins/transforms/http/src/main/java/org/apache/hop/pipeline/transforms/http/HttpData.java
 
b/plugins/transforms/http/src/main/java/org/apache/hop/pipeline/transforms/http/HttpData.java
index f5a7b8661d..447f4dd1c9 100644
--- 
a/plugins/transforms/http/src/main/java/org/apache/hop/pipeline/transforms/http/HttpData.java
+++ 
b/plugins/transforms/http/src/main/java/org/apache/hop/pipeline/transforms/http/HttpData.java
@@ -33,7 +33,7 @@ public class HttpData extends BaseTransformData implements 
ITransformData {
   public int realProxyPort;
   public String realHttpLogin;
   public String realHttpPassword;
-  public int[] header_parameters_nrs;
+  public int[] headerParametersNrs;
   public boolean useHeaderParameters;
   public NameValuePair[] headerParameters;
 
diff --git 
a/plugins/transforms/http/src/main/java/org/apache/hop/pipeline/transforms/http/HttpMeta.java
 
b/plugins/transforms/http/src/main/java/org/apache/hop/pipeline/transforms/http/HttpMeta.java
index faf8e38657..fb7c6be510 100644
--- 
a/plugins/transforms/http/src/main/java/org/apache/hop/pipeline/transforms/http/HttpMeta.java
+++ 
b/plugins/transforms/http/src/main/java/org/apache/hop/pipeline/transforms/http/HttpMeta.java
@@ -18,6 +18,8 @@
 package org.apache.hop.pipeline.transforms.http;
 
 import java.util.List;
+import lombok.Getter;
+import lombok.Setter;
 import org.apache.hop.core.CheckResult;
 import org.apache.hop.core.Const;
 import org.apache.hop.core.ICheckResult;
@@ -47,6 +49,8 @@ import org.w3c.dom.Node;
     categoryDescription = 
"i18n:org.apache.hop.pipeline.transform:BaseTransform.Category.Lookup",
     keywords = "i18n::HttpMeta.keyword",
     documentationUrl = "/pipeline/transforms/http.html")
+@Getter
+@Setter
 public class HttpMeta extends BaseTransformMeta<Http, HttpData> {
   private static final Class<?> PKG = HttpMeta.class;
 
@@ -108,162 +112,6 @@ public class HttpMeta extends BaseTransformMeta<Http, 
HttpData> {
     super(); // allocate BaseTransformMeta
   }
 
-  /**
-   * @return Returns the connectionTimeout.
-   */
-  public String getConnectionTimeout() {
-    return connectionTimeout;
-  }
-
-  /**
-   * @param connectionTimeout The connectionTimeout to set.
-   */
-  public void setConnectionTimeout(String connectionTimeout) {
-    this.connectionTimeout = connectionTimeout;
-  }
-
-  /**
-   * @return Returns the closeIdleConnectionsTime.
-   */
-  public String getCloseIdleConnectionsTime() {
-    return closeIdleConnectionsTime;
-  }
-
-  /**
-   * @param closeIdleConnectionsTime The connectionTimeout to set.
-   */
-  public void setCloseIdleConnectionsTime(String closeIdleConnectionsTime) {
-    this.closeIdleConnectionsTime = closeIdleConnectionsTime;
-  }
-
-  /**
-   * @return Returns the socketTimeout.
-   */
-  public String getSocketTimeout() {
-    return socketTimeout;
-  }
-
-  /**
-   * @param socketTimeout The socketTimeout to set.
-   */
-  public void setSocketTimeout(String socketTimeout) {
-    this.socketTimeout = socketTimeout;
-  }
-
-  /**
-   * @return Returns the argument.
-   */
-  public String[] getArgumentField() {
-    return argumentField;
-  }
-
-  /**
-   * @param argument The argument to set.
-   */
-  public void setArgumentField(String[] argument) {
-    this.argumentField = argument;
-  }
-
-  /**
-   * @return Returns the headerFields.
-   */
-  public String[] getHeaderField() {
-
-    return headerField;
-  }
-
-  /**
-   * @param headerField The headerField to set.
-   */
-  public void setHeaderField(String[] headerField) {
-
-    this.headerField = headerField;
-  }
-
-  /**
-   * @return Returns the argumentDirection.
-   */
-  public String[] getArgumentParameter() {
-    return argumentParameter;
-  }
-
-  /**
-   * @param argumentDirection The argumentDirection to set.
-   */
-  public void setArgumentParameter(String[] argumentDirection) {
-    this.argumentParameter = argumentDirection;
-  }
-
-  /**
-   * @return Returns the headerParameter.
-   */
-  public String[] getHeaderParameter() {
-    return headerParameter;
-  }
-
-  /**
-   * @param headerParameter The headerParameter to set.
-   */
-  public void setHeaderParameter(String[] headerParameter) {
-    this.headerParameter = headerParameter;
-  }
-
-  /**
-   * @return Returns the procedure.
-   */
-  public String getUrl() {
-    return url;
-  }
-
-  /**
-   * @param procedure The procedure to set.
-   */
-  public void setUrl(String procedure) {
-    this.url = procedure;
-  }
-
-  /**
-   * @return Returns the resultName.
-   */
-  public String getFieldName() {
-    return fieldName;
-  }
-
-  /**
-   * @param resultName The resultName to set.
-   */
-  public void setFieldName(String resultName) {
-    this.fieldName = resultName;
-  }
-
-  /**
-   * @return Is the url coded in a field?
-   */
-  public boolean isUrlInField() {
-    return urlInField;
-  }
-
-  /**
-   * @param urlInField Is the url coded in a field?
-   */
-  public void setUrlInField(boolean urlInField) {
-    this.urlInField = urlInField;
-  }
-
-  /**
-   * @return The field name that contains the url.
-   */
-  public String getUrlField() {
-    return urlField;
-  }
-
-  /**
-   * @param urlField name of the field that contains the url
-   */
-  public void setUrlField(String urlField) {
-    this.urlField = urlField;
-  }
-
   @Override
   public void loadXml(Node transformNode, IHopMetadataProvider 
metadataProvider)
       throws HopXmlException {
@@ -527,126 +375,4 @@ public class HttpMeta extends BaseTransformMeta<Http, 
HttpData> {
   public boolean supportsErrorHandling() {
     return true;
   }
-
-  /**
-   * @return the encoding
-   */
-  public String getEncoding() {
-    return encoding;
-  }
-
-  /**
-   * @param encoding the encoding to set
-   */
-  public void setEncoding(String encoding) {
-    this.encoding = encoding;
-  }
-
-  /**
-   * ISetter
-   *
-   * @param proxyHost
-   */
-  public void setProxyHost(String proxyHost) {
-    this.proxyHost = proxyHost;
-  }
-
-  /**
-   * IGetter
-   *
-   * @return
-   */
-  public String getProxyHost() {
-    return proxyHost;
-  }
-
-  /**
-   * ISetter
-   *
-   * @param proxyPort
-   */
-  public void setProxyPort(String proxyPort) {
-    this.proxyPort = proxyPort;
-  }
-
-  /**
-   * IGetter
-   *
-   * @return
-   */
-  public String getProxyPort() {
-    return this.proxyPort;
-  }
-
-  /**
-   * ISetter
-   *
-   * @param httpLogin
-   */
-  public void setHttpLogin(String httpLogin) {
-    this.httpLogin = httpLogin;
-  }
-
-  /**
-   * IGetter
-   *
-   * @return
-   */
-  public String getHttpLogin() {
-    return httpLogin;
-  }
-
-  /**
-   * ISetter
-   *
-   * @param httpPassword
-   */
-  public void setHttpPassword(String httpPassword) {
-    this.httpPassword = httpPassword;
-  }
-
-  /**
-   * @return
-   */
-  public String getHttpPassword() {
-    return httpPassword;
-  }
-
-  /**
-   * @return the resultCodeFieldName
-   */
-  public String getResultCodeFieldName() {
-    return resultCodeFieldName;
-  }
-
-  /**
-   * @param resultCodeFieldName the resultCodeFieldName to set
-   */
-  public void setResultCodeFieldName(String resultCodeFieldName) {
-    this.resultCodeFieldName = resultCodeFieldName;
-  }
-
-  public String getResponseTimeFieldName() {
-    return responseTimeFieldName;
-  }
-
-  public void setResponseTimeFieldName(String responseTimeFieldName) {
-    this.responseTimeFieldName = responseTimeFieldName;
-  }
-
-  public String getResponseHeaderFieldName() {
-    return responseHeaderFieldName;
-  }
-
-  public void setResponseHeaderFieldName(String responseHeaderFieldName) {
-    this.responseHeaderFieldName = responseHeaderFieldName;
-  }
-
-  public boolean isIgnoreSsl() {
-    return ignoreSsl;
-  }
-
-  public void setIgnoreSsl(boolean ignoreSsl) {
-    this.ignoreSsl = ignoreSsl;
-  }
 }
diff --git 
a/plugins/transforms/http/src/test/java/org/apache/hop/pipeline/transforms/http/HttpDataTest.java
 
b/plugins/transforms/http/src/test/java/org/apache/hop/pipeline/transforms/http/HttpDataTest.java
new file mode 100644
index 0000000000..a222711b6b
--- /dev/null
+++ 
b/plugins/transforms/http/src/test/java/org/apache/hop/pipeline/transforms/http/HttpDataTest.java
@@ -0,0 +1,255 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.hop.pipeline.transforms.http;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.apache.hop.core.row.RowMeta;
+import org.apache.hop.junit.rules.RestoreHopEnvironmentExtension;
+import org.apache.http.NameValuePair;
+import org.apache.http.message.BasicNameValuePair;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+
+@ExtendWith(RestoreHopEnvironmentExtension.class)
+class HttpDataTest {
+
+  private HttpData data;
+
+  @BeforeEach
+  void setUp() {
+    data = new HttpData();
+  }
+
+  @Test
+  void testDefaultConstructor() {
+    assertNotNull(data, "HttpData should not be null");
+    assertEquals(-1, data.indexOfUrlField, "indexOfUrlField should be -1 by 
default");
+    assertNull(data.realProxyHost, "realProxyHost should be null by default");
+    assertEquals(8080, data.realProxyPort, "realProxyPort should be 8080 by 
default");
+    assertNull(data.realHttpLogin, "realHttpLogin should be null by default");
+    assertNull(data.realHttpPassword, "realHttpPassword should be null by 
default");
+  }
+
+  @Test
+  void testIndexOfUrlField() {
+    data.indexOfUrlField = 5;
+    assertEquals(5, data.indexOfUrlField);
+
+    data.indexOfUrlField = -1;
+    assertEquals(-1, data.indexOfUrlField);
+  }
+
+  @Test
+  void testRealUrl() {
+    assertNull(data.realUrl, "realUrl should be null initially");
+
+    data.realUrl = "http://example.com";;
+    assertEquals("http://example.com";, data.realUrl);
+  }
+
+  @Test
+  void testRealProxyHost() {
+    assertNull(data.realProxyHost, "realProxyHost should be null by default");
+
+    data.realProxyHost = "proxy.example.com";
+    assertEquals("proxy.example.com", data.realProxyHost);
+  }
+
+  @Test
+  void testRealProxyPort() {
+    assertEquals(8080, data.realProxyPort, "Default proxy port should be 
8080");
+
+    data.realProxyPort = 3128;
+    assertEquals(3128, data.realProxyPort);
+  }
+
+  @Test
+  void testRealHttpLogin() {
+    assertNull(data.realHttpLogin, "realHttpLogin should be null by default");
+
+    data.realHttpLogin = "username";
+    assertEquals("username", data.realHttpLogin);
+  }
+
+  @Test
+  void testRealHttpPassword() {
+    assertNull(data.realHttpPassword, "realHttpPassword should be null by 
default");
+
+    data.realHttpPassword = "password123";
+    assertEquals("password123", data.realHttpPassword);
+  }
+
+  @Test
+  void testArgnrs() {
+    assertNull(data.argnrs, "argnrs should be null initially");
+
+    data.argnrs = new int[] {0, 1, 2};
+    assertEquals(3, data.argnrs.length);
+    assertEquals(0, data.argnrs[0]);
+    assertEquals(1, data.argnrs[1]);
+    assertEquals(2, data.argnrs[2]);
+  }
+
+  @Test
+  void testOutputRowMeta() {
+    assertNull(data.outputRowMeta, "outputRowMeta should be null initially");
+
+    data.outputRowMeta = new RowMeta();
+    assertNotNull(data.outputRowMeta);
+  }
+
+  @Test
+  void testInputRowMeta() {
+    assertNull(data.inputRowMeta, "inputRowMeta should be null initially");
+
+    data.inputRowMeta = new RowMeta();
+    assertNotNull(data.inputRowMeta);
+  }
+
+  @Test
+  void testHeaderParametersNrs() {
+    assertNull(data.headerParametersNrs, "headerParametersNrs should be null 
initially");
+
+    data.headerParametersNrs = new int[] {1, 3, 5};
+    assertEquals(3, data.headerParametersNrs.length);
+    assertEquals(1, data.headerParametersNrs[0]);
+    assertEquals(3, data.headerParametersNrs[1]);
+    assertEquals(5, data.headerParametersNrs[2]);
+  }
+
+  @Test
+  void testUseHeaderParameters() {
+    assertFalse(data.useHeaderParameters, "useHeaderParameters should be false 
by default");
+
+    data.useHeaderParameters = true;
+    assertEquals(true, data.useHeaderParameters);
+  }
+
+  @Test
+  void testHeaderParameters() {
+    assertNull(data.headerParameters, "headerParameters should be null 
initially");
+
+    NameValuePair[] params =
+        new NameValuePair[] {
+          new BasicNameValuePair("Authorization", "Bearer token"),
+          new BasicNameValuePair("Content-Type", "application/json")
+        };
+    data.headerParameters = params;
+
+    assertNotNull(data.headerParameters);
+    assertEquals(2, data.headerParameters.length);
+    assertEquals("Authorization", data.headerParameters[0].getName());
+    assertEquals("Bearer token", data.headerParameters[0].getValue());
+  }
+
+  @Test
+  void testRealSocketTimeout() {
+    assertEquals(0, data.realSocketTimeout, "realSocketTimeout should be 0 
initially");
+
+    data.realSocketTimeout = 5000;
+    assertEquals(5000, data.realSocketTimeout);
+  }
+
+  @Test
+  void testRealConnectionTimeout() {
+    assertEquals(0, data.realConnectionTimeout, "realConnectionTimeout should 
be 0 initially");
+
+    data.realConnectionTimeout = 3000;
+    assertEquals(3000, data.realConnectionTimeout);
+  }
+
+  @Test
+  void testWithoutPreviousTransforms() {
+    assertFalse(
+        data.withoutPreviousTransforms, "withoutPreviousTransforms should be 
false by default");
+
+    data.withoutPreviousTransforms = true;
+    assertTrue(data.withoutPreviousTransforms);
+  }
+
+  @Test
+  void testMultipleFieldsSetAndGet() {
+    // Set multiple fields
+    data.indexOfUrlField = 10;
+    data.realUrl = "http://api.example.com";;
+    data.realProxyHost = "proxy.corp.com";
+    data.realProxyPort = 9090;
+    data.realHttpLogin = "admin";
+    data.realHttpPassword = "secret";
+    data.realSocketTimeout = 15000;
+    data.realConnectionTimeout = 8000;
+    data.useHeaderParameters = true;
+    data.withoutPreviousTransforms = true;
+
+    // Verify all fields
+    assertEquals(10, data.indexOfUrlField);
+    assertEquals("http://api.example.com";, data.realUrl);
+    assertEquals("proxy.corp.com", data.realProxyHost);
+    assertEquals(9090, data.realProxyPort);
+    assertEquals("admin", data.realHttpLogin);
+    assertEquals("secret", data.realHttpPassword);
+    assertEquals(15000, data.realSocketTimeout);
+    assertEquals(8000, data.realConnectionTimeout);
+    assertTrue(data.useHeaderParameters);
+    assertTrue(data.withoutPreviousTransforms);
+  }
+
+  @Test
+  void testArgnrsWithEmptyArray() {
+    data.argnrs = new int[0];
+    assertNotNull(data.argnrs);
+    assertEquals(0, data.argnrs.length);
+  }
+
+  @Test
+  void testHeaderParametersWithEmptyArray() {
+    data.headerParameters = new NameValuePair[0];
+    assertNotNull(data.headerParameters);
+    assertEquals(0, data.headerParameters.length);
+  }
+
+  @Test
+  void testNegativeIndexOfUrlField() {
+    data.indexOfUrlField = -999;
+    assertEquals(-999, data.indexOfUrlField);
+  }
+
+  @Test
+  void testZeroTimeouts() {
+    data.realSocketTimeout = 0;
+    data.realConnectionTimeout = 0;
+
+    assertEquals(0, data.realSocketTimeout);
+    assertEquals(0, data.realConnectionTimeout);
+  }
+
+  @Test
+  void testLargeTimeouts() {
+    data.realSocketTimeout = Integer.MAX_VALUE;
+    data.realConnectionTimeout = Integer.MAX_VALUE;
+
+    assertEquals(Integer.MAX_VALUE, data.realSocketTimeout);
+    assertEquals(Integer.MAX_VALUE, data.realConnectionTimeout);
+  }
+}
diff --git 
a/plugins/transforms/http/src/test/java/org/apache/hop/pipeline/transforms/http/HttpMetaTest.java
 
b/plugins/transforms/http/src/test/java/org/apache/hop/pipeline/transforms/http/HttpMetaTest.java
new file mode 100644
index 0000000000..f78f5f2ec3
--- /dev/null
+++ 
b/plugins/transforms/http/src/test/java/org/apache/hop/pipeline/transforms/http/HttpMetaTest.java
@@ -0,0 +1,341 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.hop.pipeline.transforms.http;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.mock;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.hop.core.ICheckResult;
+import org.apache.hop.core.row.IRowMeta;
+import org.apache.hop.core.row.RowMeta;
+import org.apache.hop.core.variables.Variables;
+import org.apache.hop.junit.rules.RestoreHopEnvironmentExtension;
+import org.apache.hop.pipeline.PipelineMeta;
+import org.apache.hop.pipeline.transform.TransformMeta;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+
+@ExtendWith(RestoreHopEnvironmentExtension.class)
+class HttpMetaTest {
+
+  private HttpMeta meta;
+
+  @BeforeEach
+  void setUp() {
+    meta = new HttpMeta();
+  }
+
+  @Test
+  void testSetDefault() {
+    meta.setDefault();
+
+    assertEquals(String.valueOf(HttpMeta.DEFAULT_SOCKET_TIMEOUT), 
meta.getSocketTimeout());
+    assertEquals(String.valueOf(HttpMeta.DEFAULT_CONNECTION_TIMEOUT), 
meta.getConnectionTimeout());
+    assertEquals(
+        String.valueOf(HttpMeta.DEFAULT_CLOSE_CONNECTIONS_TIME),
+        meta.getCloseIdleConnectionsTime());
+    assertEquals("result", meta.getFieldName());
+    assertEquals("UTF-8", meta.getEncoding());
+    assertEquals("", meta.getResultCodeFieldName());
+    assertEquals("", meta.getResponseTimeFieldName());
+    assertEquals("", meta.getResponseHeaderFieldName());
+  }
+
+  @Test
+  void testUrlConfiguration() {
+    // Test URL properties
+    meta.setUrl("http://example.com";);
+    assertEquals("http://example.com";, meta.getUrl());
+
+    meta.setUrlInField(true);
+    assertTrue(meta.isUrlInField());
+
+    meta.setUrlField("urlColumn");
+    assertEquals("urlColumn", meta.getUrlField());
+
+    meta.setUrlInField(false);
+    assertFalse(meta.isUrlInField());
+  }
+
+  @Test
+  void testOutputFieldConfiguration() {
+    // Test all output field properties
+    meta.setFieldName("resultField");
+    assertEquals("resultField", meta.getFieldName());
+
+    meta.setResultCodeFieldName("responseCode");
+    assertEquals("responseCode", meta.getResultCodeFieldName());
+
+    meta.setResponseTimeFieldName("responseTime");
+    assertEquals("responseTime", meta.getResponseTimeFieldName());
+
+    meta.setResponseHeaderFieldName("responseHeader");
+    assertEquals("responseHeader", meta.getResponseHeaderFieldName());
+  }
+
+  @Test
+  void testProxyAndAuthConfiguration() {
+    // Test proxy and authentication properties
+    meta.setProxyHost("proxy.example.com");
+    assertEquals("proxy.example.com", meta.getProxyHost());
+
+    meta.setProxyPort("8080");
+    assertEquals("8080", meta.getProxyPort());
+
+    meta.setHttpLogin("username");
+    assertEquals("username", meta.getHttpLogin());
+
+    meta.setHttpPassword("password123");
+    assertEquals("password123", meta.getHttpPassword());
+  }
+
+  @Test
+  void testTimeoutAndConnectionConfiguration() {
+    // Test timeout and connection properties
+    meta.setSocketTimeout("5000");
+    assertEquals("5000", meta.getSocketTimeout());
+
+    meta.setConnectionTimeout("3000");
+    assertEquals("3000", meta.getConnectionTimeout());
+
+    meta.setCloseIdleConnectionsTime("10000");
+    assertEquals("10000", meta.getCloseIdleConnectionsTime());
+
+    meta.setEncoding("ISO-8859-1");
+    assertEquals("ISO-8859-1", meta.getEncoding());
+
+    meta.setIgnoreSsl(true);
+    assertTrue(meta.isIgnoreSsl());
+
+    meta.setIgnoreSsl(false);
+    assertFalse(meta.isIgnoreSsl());
+  }
+
+  @Test
+  void testArgumentsAndHeadersConfiguration() {
+    // Test allocate
+    meta.allocate(3, 2);
+    assertNotNull(meta.getArgumentField());
+    assertNotNull(meta.getArgumentParameter());
+    assertNotNull(meta.getHeaderField());
+    assertNotNull(meta.getHeaderParameter());
+    assertEquals(3, meta.getArgumentField().length);
+    assertEquals(3, meta.getArgumentParameter().length);
+    assertEquals(2, meta.getHeaderField().length);
+    assertEquals(2, meta.getHeaderParameter().length);
+
+    // Test argument fields and parameters
+    String[] argFields = {"field1", "field2", "field3"};
+    meta.setArgumentField(argFields);
+    assertEquals(3, meta.getArgumentField().length);
+    assertEquals("field1", meta.getArgumentField()[0]);
+    assertEquals("field2", meta.getArgumentField()[1]);
+
+    String[] argParams = {"param1", "param2"};
+    meta.setArgumentParameter(argParams);
+    assertEquals("param1", meta.getArgumentParameter()[0]);
+    assertEquals("param2", meta.getArgumentParameter()[1]);
+
+    // Test header fields and parameters
+    String[] headerFields = {"Authorization", "Content-Type"};
+    meta.setHeaderField(headerFields);
+    assertEquals(2, meta.getHeaderField().length);
+    assertEquals("Authorization", meta.getHeaderField()[0]);
+
+    String[] headerParams = {"Bearer token", "application/json"};
+    meta.setHeaderParameter(headerParams);
+    assertEquals(2, meta.getHeaderParameter().length);
+    assertEquals("Bearer token", meta.getHeaderParameter()[0]);
+  }
+
+  @Test
+  void testClone() {
+    meta.allocate(2, 1);
+    meta.setUrl("http://test.com";);
+    meta.setFieldName("output");
+    meta.getArgumentField()[0] = "arg1";
+    meta.getArgumentField()[1] = "arg2";
+    meta.getArgumentParameter()[0] = "param1";
+    meta.getArgumentParameter()[1] = "param2";
+    meta.getHeaderField()[0] = "header1";
+    meta.getHeaderParameter()[0] = "value1";
+
+    HttpMeta cloned = (HttpMeta) meta.clone();
+
+    assertNotNull(cloned);
+    assertEquals(meta.getUrl(), cloned.getUrl());
+    assertEquals(meta.getFieldName(), cloned.getFieldName());
+    assertEquals(2, cloned.getArgumentField().length);
+    assertEquals("arg1", cloned.getArgumentField()[0]);
+    assertEquals("arg2", cloned.getArgumentField()[1]);
+  }
+
+  @Test
+  void testGetFields() throws Exception {
+    Variables variables = new Variables();
+
+    // Test with all fields configured
+    meta.setFieldName("resultField");
+    meta.setResultCodeFieldName("statusCode");
+    meta.setResponseTimeFieldName("responseTime");
+    meta.setResponseHeaderFieldName("headers");
+
+    IRowMeta rowMetaAll = new RowMeta();
+    meta.getFields(rowMetaAll, "HttpTransform", null, null, variables, null);
+    assertEquals(4, rowMetaAll.size());
+    assertNotNull(rowMetaAll.searchValueMeta("resultField"));
+    assertNotNull(rowMetaAll.searchValueMeta("statusCode"));
+    assertNotNull(rowMetaAll.searchValueMeta("responseTime"));
+    assertNotNull(rowMetaAll.searchValueMeta("headers"));
+    assertEquals("HttpTransform", 
rowMetaAll.searchValueMeta("resultField").getOrigin());
+
+    // Test with empty field names
+    meta.setFieldName("");
+    meta.setResultCodeFieldName("");
+    meta.setResponseTimeFieldName("");
+    meta.setResponseHeaderFieldName("");
+    IRowMeta rowMetaEmpty = new RowMeta();
+    meta.getFields(rowMetaEmpty, "HttpTransform", null, null, variables, null);
+    assertEquals(0, rowMetaEmpty.size());
+
+    // Test with only result field
+    meta.setFieldName("result");
+    IRowMeta rowMetaSingle = new RowMeta();
+    meta.getFields(rowMetaSingle, "HttpTransform", null, null, variables, 
null);
+    assertEquals(1, rowMetaSingle.size());
+    assertNotNull(rowMetaSingle.searchValueMeta("result"));
+  }
+
+  @Test
+  void testGetXml() {
+    meta.allocate(1, 1);
+    meta.setUrl("http://example.com";);
+    meta.setUrlInField(true);
+    meta.setUrlField("urlField");
+    meta.setEncoding("UTF-8");
+    meta.setHttpLogin("user");
+    meta.setHttpPassword("pass");
+    meta.setProxyHost("proxy");
+    meta.setProxyPort("8080");
+    meta.getArgumentField()[0] = "arg1";
+    meta.getArgumentParameter()[0] = "param1";
+    meta.getHeaderField()[0] = "header1";
+    meta.getHeaderParameter()[0] = "value1";
+    meta.setFieldName("result");
+    meta.setResultCodeFieldName("code");
+
+    String xml = meta.getXml();
+
+    assertNotNull(xml);
+    assertTrue(xml.contains("<url>http://example.com</url>"));
+    assertTrue(xml.contains("<urlInField>Y</urlInField>"));
+    assertTrue(xml.contains("<urlField>urlField</urlField>"));
+    assertTrue(xml.contains("<encoding>UTF-8</encoding>"));
+    assertTrue(xml.contains("<httpLogin>user</httpLogin>"));
+    assertTrue(xml.contains("<proxyHost>proxy</proxyHost>"));
+    assertTrue(xml.contains("<proxyPort>8080</proxyPort>"));
+    assertTrue(xml.contains("<name>arg1</name>"));
+    assertTrue(xml.contains("<parameter>param1</parameter>"));
+    assertTrue(xml.contains("<name>result</name>"));
+    assertTrue(xml.contains("<code>code</code>"));
+  }
+
+  @Test
+  void testCheckValidation() {
+    TransformMeta transformMeta = mock(TransformMeta.class);
+    PipelineMeta pipelineMeta = mock(PipelineMeta.class);
+    IRowMeta prev = mock(IRowMeta.class);
+    List<ICheckResult> remarks;
+
+    // Test with input transform
+    remarks = new ArrayList<>();
+    meta.check(
+        remarks, pipelineMeta, transformMeta, prev, new String[] {"prev"}, 
null, null, null, null);
+    assertTrue(remarks.size() > 0);
+    assertTrue(remarks.stream().anyMatch(r -> r.getType() == 
ICheckResult.TYPE_RESULT_OK));
+
+    // Test without input transform
+    remarks = new ArrayList<>();
+    meta.check(remarks, pipelineMeta, transformMeta, prev, new String[] {}, 
null, null, null, null);
+    assertTrue(remarks.stream().anyMatch(r -> r.getType() == 
ICheckResult.TYPE_RESULT_ERROR));
+
+    // Test URL in field - missing field name
+    remarks = new ArrayList<>();
+    meta.setUrlInField(true);
+    meta.setUrlField("");
+    meta.check(
+        remarks, pipelineMeta, transformMeta, prev, new String[] {"prev"}, 
null, null, null, null);
+    assertTrue(remarks.stream().anyMatch(r -> r.getType() == 
ICheckResult.TYPE_RESULT_ERROR));
+
+    // Test URL in field - with field name
+    remarks = new ArrayList<>();
+    meta.setUrlField("urlColumn");
+    meta.check(
+        remarks, pipelineMeta, transformMeta, prev, new String[] {"prev"}, 
null, null, null, null);
+    assertFalse(remarks.isEmpty());
+
+    // Test URL not in field - missing URL
+    remarks = new ArrayList<>();
+    meta.setUrlInField(false);
+    meta.setUrl("");
+    meta.check(
+        remarks, pipelineMeta, transformMeta, prev, new String[] {"prev"}, 
null, null, null, null);
+    assertTrue(remarks.stream().anyMatch(r -> r.getType() == 
ICheckResult.TYPE_RESULT_ERROR));
+
+    // Test URL not in field - with URL
+    remarks = new ArrayList<>();
+    meta.setUrl("http://example.com";);
+    meta.check(
+        remarks, pipelineMeta, transformMeta, prev, new String[] {"prev"}, 
null, null, null, null);
+    assertTrue(remarks.stream().anyMatch(r -> r.getType() == 
ICheckResult.TYPE_RESULT_OK));
+  }
+
+  @Test
+  void testMiscellaneous() {
+    // Test error handling support
+    assertTrue(meta.supportsErrorHandling());
+
+    // Test constants
+    assertEquals(10000, HttpMeta.DEFAULT_SOCKET_TIMEOUT);
+    assertEquals(10000, HttpMeta.DEFAULT_CONNECTION_TIMEOUT);
+    assertEquals(HttpMeta.DEFAULT_CLOSE_CONNECTIONS_TIME, -1);
+    assertEquals("header", HttpMeta.CONST_HEADER);
+    assertEquals("result", HttpMeta.CONST_RESULT);
+    assertEquals("        ", HttpMeta.CONST_SPACES_LONG);
+    assertEquals("      ", HttpMeta.CONST_SPACES);
+    assertEquals("parameter", HttpMeta.CONST_PARAMETER);
+
+    // Test allocate with zero
+    meta.allocate(0, 0);
+    assertEquals(0, meta.getArgumentField().length);
+    assertEquals(0, meta.getArgumentParameter().length);
+    assertEquals(0, meta.getHeaderField().length);
+    assertEquals(0, meta.getHeaderParameter().length);
+
+    // Test clone without allocation
+    HttpMeta cloned = (HttpMeta) meta.clone();
+    assertNotNull(cloned);
+    assertEquals(0, cloned.getArgumentField().length);
+  }
+}
diff --git 
a/plugins/transforms/http/src/test/java/org/apache/hop/pipeline/transforms/http/HttpTest.java
 
b/plugins/transforms/http/src/test/java/org/apache/hop/pipeline/transforms/http/HttpTest.java
index a24d82f095..f73c4dd0a1 100644
--- 
a/plugins/transforms/http/src/test/java/org/apache/hop/pipeline/transforms/http/HttpTest.java
+++ 
b/plugins/transforms/http/src/test/java/org/apache/hop/pipeline/transforms/http/HttpTest.java
@@ -18,102 +18,653 @@
 package org.apache.hop.pipeline.transforms.http;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.doCallRealMethod;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.mockStatic;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
 
 import java.io.ByteArrayInputStream;
 import java.net.HttpURLConnection;
-import org.apache.hop.core.logging.ILogChannel;
+import java.nio.charset.StandardCharsets;
+import org.apache.hop.core.logging.ILoggingObject;
 import org.apache.hop.core.row.IRowMeta;
+import org.apache.hop.core.row.RowMeta;
+import org.apache.hop.core.row.value.ValueMetaString;
 import org.apache.hop.core.util.HttpClientManager;
+import org.apache.hop.junit.rules.RestoreHopEnvironmentExtension;
+import org.apache.hop.pipeline.transforms.mock.TransformMockHelper;
 import org.apache.http.Header;
+import org.apache.http.HttpEntity;
 import org.apache.http.HttpHost;
 import org.apache.http.HttpRequest;
+import org.apache.http.StatusLine;
 import org.apache.http.client.methods.CloseableHttpResponse;
-import org.apache.http.entity.BasicHttpEntity;
 import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.message.BasicHeader;
 import org.apache.http.protocol.HttpContext;
-import org.junit.jupiter.api.AfterAll;
-import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
 import org.mockito.MockedStatic;
 
+@ExtendWith(RestoreHopEnvironmentExtension.class)
 class HttpTest {
 
-  private static MockedStatic<HttpClientManager> mockedHttpClientManager;
+  private TransformMockHelper<HttpMeta, HttpData> transformMockHelper;
+  private Http httpTransform;
+  private HttpMeta httpMeta;
+  private HttpData httpData;
 
-  private final ILogChannel log = mock(ILogChannel.class);
-  private final IRowMeta rmi = mock(IRowMeta.class);
-  private final HttpData data = mock(HttpData.class);
-  private final HttpMeta meta = mock(HttpMeta.class);
-  private final Http http = mock(Http.class);
-
-  private final String DATA =
-      "This is the description, there's some HTML here, like 
&lt;strong&gt;this&lt;/strong&gt;. "
-          + "Sometimes this text is another language that might contain these 
characters:\n"
-          + "&lt;p&gt;é, è, ô, ç, à, ê, â.&lt;/p&gt; They can, of course, come 
in uppercase as well: &lt;p&gt;É, È Ô, Ç, À,"
-          + " Ê, Â&lt;/p&gt;. UTF-8 handles this well.";
+  private static final String TEST_RESPONSE_BODY = "Test response from HTTP 
server";
+  private static final String TEST_URL = "http://example.com/api";;
 
   @BeforeEach
-  void setup() throws Exception {
-    HttpClientManager.HttpClientBuilderFacade builder =
-        mock(HttpClientManager.HttpClientBuilderFacade.class);
+  void setUp() throws Exception {
+    transformMockHelper = new TransformMockHelper<>("HTTP Test", 
HttpMeta.class, HttpData.class);
+    when(transformMockHelper.logChannelFactory.create(any(), 
any(ILoggingObject.class)))
+        .thenReturn(transformMockHelper.iLogChannel);
+    when(transformMockHelper.pipeline.isRunning()).thenReturn(true);
+
+    // Mock getPrevTransforms to return an array with one element by default
+    when(transformMockHelper.pipelineMeta.getPrevTransforms(any()))
+        .thenReturn(new org.apache.hop.pipeline.transform.TransformMeta[1]);
+
+    httpMeta = new HttpMeta();
+    httpMeta.setDefault();
+    httpMeta.setUrl(TEST_URL);
+    httpMeta.setFieldName("response");
+    httpMeta.allocate(0, 0);
+
+    httpData = new HttpData();
+
+    httpTransform =
+        new Http(
+            transformMockHelper.transformMeta,
+            httpMeta,
+            httpData,
+            0,
+            transformMockHelper.pipelineMeta,
+            transformMockHelper.pipeline);
+  }
+
+  @AfterEach
+  void tearDown() {
+    transformMockHelper.cleanUp();
+  }
+
+  @Test
+  void testProcessRowWithNullRow() throws Exception {
+    // Create a spy to mock getRow()
+    Http httpSpy = spy(httpTransform);
+    doReturn(null).when(httpSpy).getRow();
+
+    // Process row - should return false when no more rows
+    boolean result = httpSpy.processRow();
+
+    assertFalse(result, "processRow should return false when row is null");
+  }
+
+  @Test
+  void testProcessRowFullExecution() throws Exception {
+    // Configure meta
+    httpMeta.setUrl(TEST_URL);
+    httpMeta.setFieldName("responseBody");
+    httpMeta.setResultCodeFieldName("statusCode");
+    httpMeta.allocate(0, 0);
+
+    // Set up input row meta
+    IRowMeta inputRowMeta = new RowMeta();
+    inputRowMeta.addValueMeta(new ValueMetaString("id"));
+    Object[] inputRow = new Object[] {"test123"};
+
+    // Mock HttpClientManager for the full execution
+    try (MockedStatic<HttpClientManager> mockedManager = 
mockStatic(HttpClientManager.class)) {
+      HttpClientManager mockManager = mock(HttpClientManager.class);
+      HttpClientManager.HttpClientBuilderFacade mockBuilder =
+          mock(HttpClientManager.HttpClientBuilderFacade.class);
+      CloseableHttpClient mockClient = mock(CloseableHttpClient.class);
+      CloseableHttpResponse mockResponse = mock(CloseableHttpResponse.class);
+
+      
mockedManager.when(HttpClientManager::getInstance).thenReturn(mockManager);
+      when(mockManager.createBuilder()).thenReturn(mockBuilder);
+      doReturn(mockBuilder).when(mockBuilder).setConnectionTimeout(anyInt());
+      doReturn(mockBuilder).when(mockBuilder).setSocketTimeout(anyInt());
+      doReturn(mockBuilder).when(mockBuilder).setCredentials(anyString(), 
anyString());
+      doReturn(mockBuilder).when(mockBuilder).setProxy(anyString(), anyInt());
+      
org.mockito.Mockito.doNothing().when(mockBuilder).ignoreSsl(anyBoolean());
+      when(mockBuilder.build()).thenReturn(mockClient);
+
+      // Mock HTTP response
+      StatusLine mockStatusLine = mock(StatusLine.class);
+      
when(mockStatusLine.getStatusCode()).thenReturn(HttpURLConnection.HTTP_OK);
+      when(mockResponse.getStatusLine()).thenReturn(mockStatusLine);
+
+      HttpEntity mockEntity = mock(HttpEntity.class);
+      when(mockEntity.getContent())
+          .thenReturn(
+              new ByteArrayInputStream("Success 
response".getBytes(StandardCharsets.UTF_8)));
+      when(mockResponse.getEntity()).thenReturn(mockEntity);
+      when(mockResponse.getAllHeaders()).thenReturn(new Header[0]);
+
+      when(mockClient.execute(any(HttpHost.class), any(HttpRequest.class), 
any(HttpContext.class)))
+          .thenReturn(mockResponse);
+
+      // Create spy to mock getRow and capture putRow
+      Http httpSpy = spy(httpTransform);
+      doReturn(inputRow).doReturn(null).when(httpSpy).getRow();
+      doReturn(inputRowMeta).when(httpSpy).getInputRowMeta();
+
+      // Capture output
+      final Object[][] outputRows = new Object[1][];
+      org.mockito.Mockito.doAnswer(
+              invocation -> {
+                outputRows[0] = invocation.getArgument(1);
+                return null;
+              })
+          .when(httpSpy)
+          .putRow(any(IRowMeta.class), any(Object[].class));
+
+      // Initialize and process
+      httpSpy.init();
+      boolean result1 = httpSpy.processRow();
+      boolean result2 = httpSpy.processRow();
+
+      // Verify
+      assertTrue(result1, "First processRow should return true");
+      assertFalse(result2, "Second processRow should return false (no more 
rows)");
+      assertNotNull(outputRows[0], "Output row should be captured");
+      assertTrue(outputRows[0].length >= 3, "Output should have input + 
response fields");
+      assertEquals("test123", outputRows[0][0], "First field should be input 
ID");
+      assertEquals("Success response", outputRows[0][1], "Second field should 
be response body");
+      assertEquals(200L, outputRows[0][2], "Third field should be status 
code");
+    }
+  }
+
+  @Test
+  void testProcessRowWithQueryParameters() throws Exception {
+    // Configure meta with query parameters
+    httpMeta.setUrl(TEST_URL);
+    httpMeta.setFieldName("response");
+    httpMeta.allocate(2, 0);
+    httpMeta.getArgumentField()[0] = "userId";
+    httpMeta.getArgumentField()[1] = "action";
+    httpMeta.getArgumentParameter()[0] = "id";
+    httpMeta.getArgumentParameter()[1] = "action";
+
+    // Set up input row meta
+    IRowMeta inputRowMeta = new RowMeta();
+    inputRowMeta.addValueMeta(new ValueMetaString("userId"));
+    inputRowMeta.addValueMeta(new ValueMetaString("action"));
+    Object[] inputRow = new Object[] {"user123", "view"};
+
+    // Mock HttpClientManager
+    try (MockedStatic<HttpClientManager> mockedManager = 
mockStatic(HttpClientManager.class)) {
+      HttpClientManager mockManager = mock(HttpClientManager.class);
+      HttpClientManager.HttpClientBuilderFacade mockBuilder =
+          mock(HttpClientManager.HttpClientBuilderFacade.class);
+      CloseableHttpClient mockClient = mock(CloseableHttpClient.class);
+      CloseableHttpResponse mockResponse = mock(CloseableHttpResponse.class);
+
+      
mockedManager.when(HttpClientManager::getInstance).thenReturn(mockManager);
+      when(mockManager.createBuilder()).thenReturn(mockBuilder);
+      doReturn(mockBuilder).when(mockBuilder).setConnectionTimeout(anyInt());
+      doReturn(mockBuilder).when(mockBuilder).setSocketTimeout(anyInt());
+      doReturn(mockBuilder).when(mockBuilder).setCredentials(anyString(), 
anyString());
+      doReturn(mockBuilder).when(mockBuilder).setProxy(anyString(), anyInt());
+      
org.mockito.Mockito.doNothing().when(mockBuilder).ignoreSsl(anyBoolean());
+      when(mockBuilder.build()).thenReturn(mockClient);
+
+      StatusLine mockStatusLine = mock(StatusLine.class);
+      
when(mockStatusLine.getStatusCode()).thenReturn(HttpURLConnection.HTTP_OK);
+      when(mockResponse.getStatusLine()).thenReturn(mockStatusLine);
+
+      HttpEntity mockEntity = mock(HttpEntity.class);
+      when(mockEntity.getContent())
+          .thenReturn(new ByteArrayInputStream("Query 
result".getBytes(StandardCharsets.UTF_8)));
+      when(mockResponse.getEntity()).thenReturn(mockEntity);
+      when(mockResponse.getAllHeaders()).thenReturn(new Header[0]);
+
+      when(mockClient.execute(any(HttpHost.class), any(HttpRequest.class), 
any(HttpContext.class)))
+          .thenReturn(mockResponse);
+
+      // Create spy
+      Http httpSpy = spy(httpTransform);
+      doReturn(inputRow).doReturn(null).when(httpSpy).getRow();
+      doReturn(inputRowMeta).when(httpSpy).getInputRowMeta();
+
+      final Object[][] outputRows = new Object[1][];
+      org.mockito.Mockito.doAnswer(
+              invocation -> {
+                outputRows[0] = invocation.getArgument(1);
+                return null;
+              })
+          .when(httpSpy)
+          .putRow(any(IRowMeta.class), any(Object[].class));
+
+      // Initialize and process
+      httpSpy.init();
+      boolean result = httpSpy.processRow();
+
+      // Verify
+      assertTrue(result, "processRow should return true");
+      assertNotNull(outputRows[0], "Output row should be captured");
+      assertEquals("user123", outputRows[0][0], "First field should be 
userId");
+      assertEquals("view", outputRows[0][1], "Second field should be action");
+      assertEquals("Query result", outputRows[0][2], "Third field should be 
response");
+    }
+  }
+
+  @Test
+  void testProcessRowWithHeaders() throws Exception {
+    // Configure meta with custom headers
+    httpMeta.setUrl(TEST_URL);
+    httpMeta.setFieldName("response");
+    httpMeta.allocate(0, 1);
+    httpMeta.getHeaderField()[0] = "authToken";
+    httpMeta.getHeaderParameter()[0] = "Authorization";
+
+    // Set up input row meta
+    IRowMeta inputRowMeta = new RowMeta();
+    inputRowMeta.addValueMeta(new ValueMetaString("authToken"));
+    Object[] inputRow = new Object[] {"Bearer token123"};
+
+    // Mock HttpClientManager
+    try (MockedStatic<HttpClientManager> mockedManager = 
mockStatic(HttpClientManager.class)) {
+      HttpClientManager mockManager = mock(HttpClientManager.class);
+      HttpClientManager.HttpClientBuilderFacade mockBuilder =
+          mock(HttpClientManager.HttpClientBuilderFacade.class);
+      CloseableHttpClient mockClient = mock(CloseableHttpClient.class);
+      CloseableHttpResponse mockResponse = mock(CloseableHttpResponse.class);
+
+      
mockedManager.when(HttpClientManager::getInstance).thenReturn(mockManager);
+      when(mockManager.createBuilder()).thenReturn(mockBuilder);
+      doReturn(mockBuilder).when(mockBuilder).setConnectionTimeout(anyInt());
+      doReturn(mockBuilder).when(mockBuilder).setSocketTimeout(anyInt());
+      doReturn(mockBuilder).when(mockBuilder).setCredentials(anyString(), 
anyString());
+      doReturn(mockBuilder).when(mockBuilder).setProxy(anyString(), anyInt());
+      
org.mockito.Mockito.doNothing().when(mockBuilder).ignoreSsl(anyBoolean());
+      when(mockBuilder.build()).thenReturn(mockClient);
+
+      StatusLine mockStatusLine = mock(StatusLine.class);
+      
when(mockStatusLine.getStatusCode()).thenReturn(HttpURLConnection.HTTP_OK);
+      when(mockResponse.getStatusLine()).thenReturn(mockStatusLine);
+
+      HttpEntity mockEntity = mock(HttpEntity.class);
+      when(mockEntity.getContent())
+          .thenReturn(new 
ByteArrayInputStream("Authorized".getBytes(StandardCharsets.UTF_8)));
+      when(mockResponse.getEntity()).thenReturn(mockEntity);
+      when(mockResponse.getAllHeaders()).thenReturn(new Header[0]);
+
+      when(mockClient.execute(any(HttpHost.class), any(HttpRequest.class), 
any(HttpContext.class)))
+          .thenReturn(mockResponse);
+
+      // Create spy
+      Http httpSpy = spy(httpTransform);
+      doReturn(inputRow).doReturn(null).when(httpSpy).getRow();
+      doReturn(inputRowMeta).when(httpSpy).getInputRowMeta();
+
+      final Object[][] outputRows = new Object[1][];
+      org.mockito.Mockito.doAnswer(
+              invocation -> {
+                outputRows[0] = invocation.getArgument(1);
+                return null;
+              })
+          .when(httpSpy)
+          .putRow(any(IRowMeta.class), any(Object[].class));
+
+      // Initialize and process
+      httpSpy.init();
+      boolean result = httpSpy.processRow();
+
+      // Verify
+      assertTrue(result, "processRow should return true");
+      assertNotNull(outputRows[0], "Output row should be captured");
+      assertEquals("Bearer token123", outputRows[0][0], "First field should be 
auth token");
+      assertEquals("Authorized", outputRows[0][1], "Second field should be 
response");
+    }
+  }
+
+  @Test
+  void testInit() {
+    httpMeta.setProxyHost("proxy.example.com");
+    httpMeta.setProxyPort("8080");
+    httpMeta.setHttpLogin("user");
+    httpMeta.setHttpPassword("password");
+    httpMeta.setSocketTimeout("5000");
+    httpMeta.setConnectionTimeout("3000");
+
+    boolean result = httpTransform.init();
+
+    assertTrue(result, "init should return true");
+    assertEquals("proxy.example.com", httpData.realProxyHost);
+    assertEquals(8080, httpData.realProxyPort);
+    assertEquals("user", httpData.realHttpLogin);
+    assertEquals("password", httpData.realHttpPassword);
+    assertEquals(5000, httpData.realSocketTimeout);
+    assertEquals(3000, httpData.realConnectionTimeout);
+  }
+
+  @Test
+  void testRequestStatusCode() {
+    CloseableHttpResponse response = mock(CloseableHttpResponse.class);
+    StatusLine statusLine = mock(StatusLine.class);
+    when(response.getStatusLine()).thenReturn(statusLine);
+    when(statusLine.getStatusCode()).thenReturn(200);
 
-    HttpClientManager manager = mock(HttpClientManager.class);
-    doReturn(builder).when(manager).createBuilder();
+    int statusCode = httpTransform.requestStatusCode(response);
 
-    CloseableHttpClient client = mock(CloseableHttpClient.class);
-    doReturn(client).when(builder).build();
+    assertEquals(200, statusCode);
+  }
 
+  @Test
+  void testSearchForHeaders() {
     CloseableHttpResponse response = mock(CloseableHttpResponse.class);
-    doReturn(response)
-        .when(client)
-        .execute(any(HttpHost.class), any(HttpRequest.class), 
any(HttpContext.class));
+    Header[] headers =
+        new Header[] {
+          new BasicHeader("Content-Type", "application/json"),
+          new BasicHeader("Authorization", "Bearer token")
+        };
+    when(response.getAllHeaders()).thenReturn(headers);
+
+    Header[] result = httpTransform.searchForHeaders(response);
+
+    assertNotNull(result);
+    assertEquals(2, result.length);
+    assertEquals("Content-Type", result[0].getName());
+    assertEquals("application/json", result[0].getValue());
+  }
+
+  @Test
+  void testCallHttpServiceBasicCall() throws Exception {
+    // Set up input row meta
+    IRowMeta inputRowMeta = new RowMeta();
+    inputRowMeta.addValueMeta(new ValueMetaString("id"));
+    Object[] inputRow = new Object[] {"123"};
+
+    // Configure meta
+    httpMeta.setUrl(TEST_URL);
+    httpMeta.setFieldName("result");
+    httpMeta.allocate(0, 0);
+
+    // Set up data
+    httpData.realUrl = TEST_URL;
+    httpData.realConnectionTimeout = 10000;
+    httpData.realSocketTimeout = 10000;
+    httpData.argnrs = new int[0];
+    httpData.inputRowMeta = inputRowMeta;
+    httpData.useHeaderParameters = false;
+    httpData.headerParametersNrs = new int[0];
+
+    // Mock HttpClientManager
+    try (MockedStatic<HttpClientManager> mockedManager = 
mockStatic(HttpClientManager.class)) {
+      HttpClientManager mockManager = mock(HttpClientManager.class);
+      HttpClientManager.HttpClientBuilderFacade mockBuilder =
+          mock(HttpClientManager.HttpClientBuilderFacade.class);
+      CloseableHttpClient mockClient = mock(CloseableHttpClient.class);
+      CloseableHttpResponse mockResponse = mock(CloseableHttpResponse.class);
+
+      
mockedManager.when(HttpClientManager::getInstance).thenReturn(mockManager);
+      when(mockManager.createBuilder()).thenReturn(mockBuilder);
+      doReturn(mockBuilder).when(mockBuilder).setConnectionTimeout(anyInt());
+      doReturn(mockBuilder).when(mockBuilder).setSocketTimeout(anyInt());
+      doReturn(mockBuilder).when(mockBuilder).setCredentials(anyString(), 
anyString());
+      doReturn(mockBuilder).when(mockBuilder).setProxy(anyString(), anyInt());
+      // ignoreSsl is a void method, use doNothing
+      
org.mockito.Mockito.doNothing().when(mockBuilder).ignoreSsl(anyBoolean());
+      when(mockBuilder.build()).thenReturn(mockClient);
+
+      // Mock HTTP response
+      StatusLine mockStatusLine = mock(StatusLine.class);
+      
when(mockStatusLine.getStatusCode()).thenReturn(HttpURLConnection.HTTP_OK);
+      when(mockResponse.getStatusLine()).thenReturn(mockStatusLine);
+
+      HttpEntity mockEntity = mock(HttpEntity.class);
+      when(mockEntity.getContent())
+          .thenReturn(
+              new 
ByteArrayInputStream(TEST_RESPONSE_BODY.getBytes(StandardCharsets.UTF_8)));
+      when(mockResponse.getEntity()).thenReturn(mockEntity);
+      when(mockResponse.getAllHeaders()).thenReturn(new Header[0]);
+
+      when(mockClient.execute(any(HttpHost.class), any(HttpRequest.class), 
any(HttpContext.class)))
+          .thenReturn(mockResponse);
+
+      // Call the method
+      Object[] result = httpTransform.callHttpService(inputRowMeta, inputRow);
+
+      // Verify result
+      assertNotNull(result, "Result should not be null");
+      // Result should have input + response field (method adds response body 
to end)
+      assertTrue(result.length >= 2, "Result should have at least input + 
response fields");
+      assertEquals("123", result[0], "First field should be input ID");
+      assertEquals(TEST_RESPONSE_BODY, result[1], "Second field should be 
response body");
+    }
+  }
+
+  @Test
+  void testCallHttpServiceWithResponseFields() throws Exception {
+    // Test that callHttpService prepares to return all response fields
+
+    // Configure meta with all response fields
+    httpMeta.setUrl(TEST_URL);
+    httpMeta.setFieldName("responseBody");
+    httpMeta.setResultCodeFieldName("statusCode");
+    httpMeta.setResponseTimeFieldName("responseTime");
+    httpMeta.setResponseHeaderFieldName("responseHeaders");
+    httpMeta.allocate(0, 0);
+
+    // Set up input row
+    IRowMeta inputRowMeta = new RowMeta();
+    inputRowMeta.addValueMeta(new ValueMetaString("id"));
+
+    // Prepare output row meta
+    IRowMeta outputRowMeta = inputRowMeta.clone();
+    httpMeta.getFields(outputRowMeta, "HTTP", null, null, httpTransform, null);
+
+    // Verify output fields are configured
+    assertEquals(5, outputRowMeta.size()); // 1 input + 4 output fields
+    assertNotNull(outputRowMeta.searchValueMeta("responseBody"));
+    assertNotNull(outputRowMeta.searchValueMeta("statusCode"));
+    assertNotNull(outputRowMeta.searchValueMeta("responseTime"));
+    assertNotNull(outputRowMeta.searchValueMeta("responseHeaders"));
+  }
+
+  @Test
+  void testCallHttpServiceWithQueryParameters() throws Exception {
+    // Test callHttpService setup with query parameters
+
+    // Configure with query parameters
+    httpMeta.setUrl(TEST_URL);
+    httpMeta.setFieldName("result");
+    httpMeta.allocate(2, 0);
+    httpMeta.getArgumentField()[0] = "userId";
+    httpMeta.getArgumentField()[1] = "filter";
+    httpMeta.getArgumentParameter()[0] = "id";
+    httpMeta.getArgumentParameter()[1] = "filter";
 
-    BasicHttpEntity entity = new BasicHttpEntity();
-    entity.setContent(new ByteArrayInputStream(DATA.getBytes()));
-    doReturn(entity).when(response).getEntity();
-    
mockedHttpClientManager.when(HttpClientManager::getInstance).thenReturn(manager);
+    // Set up input row
+    IRowMeta inputRowMeta = new RowMeta();
+    inputRowMeta.addValueMeta(new ValueMetaString("userId"));
+    inputRowMeta.addValueMeta(new ValueMetaString("filter"));
+    Object[] inputRow = new Object[] {"user123", "active"};
 
-    doReturn(false).when(meta).isUrlInField();
-    doReturn("body").when(meta).getFieldName();
+    // Set up data for the call
+    httpData.realUrl = TEST_URL;
+    httpData.argnrs = new int[] {0, 1};
+    httpData.inputRowMeta = inputRowMeta;
 
-    doReturn(false).when(log).isDetailed();
+    // Verify parameters are accessible for URL building
+    assertEquals("user123", inputRowMeta.getString(inputRow, 0));
+    assertEquals("active", inputRowMeta.getString(inputRow, 1));
+    assertEquals("id", httpMeta.getArgumentParameter()[0]);
+    assertEquals("filter", httpMeta.getArgumentParameter()[1]);
+  }
+
+  @Test
+  void testCallHttpServiceWithHeaders() throws Exception {
+    // Test callHttpService setup with custom headers
+
+    // Configure with custom headers
+    httpMeta.setUrl(TEST_URL);
+    httpMeta.setFieldName("result");
+    httpMeta.allocate(0, 2);
+    httpMeta.getHeaderField()[0] = "tokenField";
+    httpMeta.getHeaderField()[1] = "contentTypeField";
+    httpMeta.getHeaderParameter()[0] = "Authorization";
+    httpMeta.getHeaderParameter()[1] = "Content-Type";
 
-    doCallRealMethod().when(http).callHttpService(any(IRowMeta.class), 
any(Object[].class));
-    doReturn(HttpURLConnection.HTTP_OK)
-        .when(http)
-        .requestStatusCode(any(CloseableHttpResponse.class));
-    doReturn(new 
Header[0]).when(http).searchForHeaders(any(CloseableHttpResponse.class));
+    // Set up input row
+    IRowMeta inputRowMeta = new RowMeta();
+    inputRowMeta.addValueMeta(new ValueMetaString("tokenField"));
+    inputRowMeta.addValueMeta(new ValueMetaString("contentTypeField"));
+    Object[] inputRow = new Object[] {"Bearer token123", "application/json"};
+
+    // Set up data
+    httpData.realUrl = TEST_URL;
+    httpData.argnrs = new int[0];
+    httpData.inputRowMeta = inputRowMeta;
+    httpData.useHeaderParameters = true;
+    httpData.headerParametersNrs = new int[] {0, 1};
+
+    // Verify headers are accessible
+    assertEquals("Bearer token123", inputRowMeta.getString(inputRow, 0));
+    assertEquals("application/json", inputRowMeta.getString(inputRow, 1));
+    assertEquals("Authorization", httpMeta.getHeaderParameter()[0]);
+    assertEquals("Content-Type", httpMeta.getHeaderParameter()[1]);
   }
 
-  @BeforeAll
-  static void setUpStaticMocks() {
-    mockedHttpClientManager = mockStatic(HttpClientManager.class);
+  @Test
+  void testGetFieldsAddsOutputFields() throws Exception {
+    httpMeta.setFieldName("responseBody");
+    httpMeta.setResultCodeFieldName("statusCode");
+    httpMeta.setResponseTimeFieldName("responseTime");
+    httpMeta.setResponseHeaderFieldName("headers");
+
+    IRowMeta inputRowMeta = new RowMeta();
+    inputRowMeta.addValueMeta(new ValueMetaString("input1"));
+
+    httpMeta.getFields(inputRowMeta, "HTTP", null, null, httpTransform, null);
+
+    assertEquals(5, inputRowMeta.size(), "Should have 5 fields (1 input + 4 
output)");
+    assertNotNull(inputRowMeta.searchValueMeta("responseBody"));
+    assertNotNull(inputRowMeta.searchValueMeta("statusCode"));
+    assertNotNull(inputRowMeta.searchValueMeta("responseTime"));
+    assertNotNull(inputRowMeta.searchValueMeta("headers"));
   }
 
-  @AfterAll
-  static void tearDownStaticMocks() {
-    mockedHttpClientManager.close();
+  @Test
+  void testProcessRowWithUrlInField() throws Exception {
+    // Configure meta to use URL from field
+    httpMeta.setUrlInField(true);
+    httpMeta.setUrlField("urlColumn");
+    httpMeta.setFieldName("result");
+    httpMeta.allocate(0, 0);
+
+    // Set up input row meta
+    IRowMeta inputRowMeta = new RowMeta();
+    inputRowMeta.addValueMeta(new ValueMetaString("urlColumn"));
+
+    // Create row with URL value
+    Object[] inputRow = new Object[] {"http://example.com/test"};
+
+    // Create spy and mock getRow
+    Http httpSpy = spy(httpTransform);
+    doReturn(inputRow).doReturn(null).when(httpSpy).getRow();
+    doReturn(inputRowMeta).when(httpSpy).getInputRowMeta();
+
+    // Initialize
+    httpSpy.init();
+    httpSpy.setInputRowMeta(inputRowMeta);
+
+    // Note: Full execution would require mocking HTTP client
+    // This test verifies the URL field resolution logic
+    httpData.inputRowMeta = inputRowMeta;
+    httpData.indexOfUrlField = inputRowMeta.indexOfValue("urlColumn");
+
+    assertEquals(0, httpData.indexOfUrlField, "URL field index should be 
found");
   }
 
-  @Disabled("This test needs to be reviewed")
   @Test
-  void callHttpServiceWithUTF8Encoding() throws Exception {
-    doReturn("UTF-8").when(meta).getEncoding();
-    assertEquals(DATA, http.callHttpService(rmi, new Object[] {0})[0]);
+  void testProcessRowWithArgumentFields() throws Exception {
+    // Configure meta with argument fields
+    httpMeta.setUrl(TEST_URL);
+    httpMeta.allocate(2, 0);
+    httpMeta.getArgumentField()[0] = "param1";
+    httpMeta.getArgumentField()[1] = "param2";
+    httpMeta.getArgumentParameter()[0] = "key1";
+    httpMeta.getArgumentParameter()[1] = "key2";
+    httpMeta.setFieldName("result");
+
+    // Set up input row meta
+    IRowMeta inputRowMeta = new RowMeta();
+    inputRowMeta.addValueMeta(new ValueMetaString("param1"));
+    inputRowMeta.addValueMeta(new ValueMetaString("param2"));
+
+    // Create row with parameter values
+    Object[] inputRow = new Object[] {"value1", "value2"};
+
+    // Create spy
+    Http httpSpy = spy(httpTransform);
+    doReturn(inputRow).doReturn(null).when(httpSpy).getRow();
+    doReturn(inputRowMeta).when(httpSpy).getInputRowMeta();
+
+    // Initialize
+    httpSpy.init();
+    httpSpy.setInputRowMeta(inputRowMeta);
+
+    // Set up data for first row processing
+    httpData.inputRowMeta = inputRowMeta;
+    httpData.argnrs = new int[2];
+    httpData.argnrs[0] = inputRowMeta.indexOfValue("param1");
+    httpData.argnrs[1] = inputRowMeta.indexOfValue("param2");
+
+    assertEquals(0, httpData.argnrs[0]);
+    assertEquals(1, httpData.argnrs[1]);
   }
 
-  @Disabled("This test needs to be reviewed")
   @Test
-  void callHttpServiceWithoutEncoding() throws Exception {
-    doReturn(null).when(meta).getEncoding();
-    assertNotEquals(DATA, http.callHttpService(rmi, new Object[] {0})[0]);
+  void testProcessRowWithHeaderFields() throws Exception {
+    // Configure meta with header fields
+    httpMeta.setUrl(TEST_URL);
+    httpMeta.allocate(0, 2);
+    httpMeta.getHeaderField()[0] = "authField";
+    httpMeta.getHeaderField()[1] = "contentTypeField";
+    httpMeta.getHeaderParameter()[0] = "Authorization";
+    httpMeta.getHeaderParameter()[1] = "Content-Type";
+    httpMeta.setFieldName("result");
+
+    // Set up input row meta
+    IRowMeta inputRowMeta = new RowMeta();
+    inputRowMeta.addValueMeta(new ValueMetaString("authField"));
+    inputRowMeta.addValueMeta(new ValueMetaString("contentTypeField"));
+
+    // Create row with header values
+    Object[] inputRow = new Object[] {"Bearer token", "application/json"};
+
+    // Create spy
+    Http httpSpy = spy(httpTransform);
+    doReturn(inputRow).doReturn(null).when(httpSpy).getRow();
+    doReturn(inputRowMeta).when(httpSpy).getInputRowMeta();
+
+    // Initialize
+    httpSpy.init();
+    httpSpy.setInputRowMeta(inputRowMeta);
+
+    // Set up data
+    httpData.inputRowMeta = inputRowMeta;
+    httpData.headerParametersNrs = new int[2];
+    httpData.headerParametersNrs[0] = inputRowMeta.indexOfValue("authField");
+    httpData.headerParametersNrs[1] = 
inputRowMeta.indexOfValue("contentTypeField");
+
+    assertEquals(0, httpData.headerParametersNrs[0]);
+    assertEquals(1, httpData.headerParametersNrs[1]);
   }
 }

Reply via email to