Bug: [FEATURE] Add custom-headers support for Rustman



### Feature Description

This will open up wide range of possibility for all the developers out there to add custom headers of their wish to test out new things and what not?  


### Possible Implementation

Pass the headers in the form of a key-value pair in the run command itself such that it injects the value to the `pre-request script` of the collection dir such that after the completion, it should restore the file to its original form.  

### Have you spent some time checking if this feature request has been raised before?

- [X] I checked and didn't find a similar issue

### Have you read the Contributing Guidelines?

- [X] I have read the [Contributing Guidelines](https://github.com/juspay/hyperswitch/blob/main/docs/CONTRIBUTING.md)

### Are you willing to submit a PR?

Yes, I am willing to submit a PR!
diff --git a/crates/test_utils/README.md b/crates/test_utils/README.md
index 1e92174b333..2edbc7104c2 100644
--- a/crates/test_utils/README.md
+++ b/crates/test_utils/README.md
@@ -28,9 +28,16 @@ Required fields:
 
 Optional fields:
 
+- `--delay` -- To add a delay between requests in milliseconds.
+  - Maximum delay is 4294967295 milliseconds or 4294967.295 seconds or 71616 minutes or 1193.6 hours or 49.733 days
+  - Example: `--delay 1000` (for 1 second delay)
 - `--folder` -- To run individual folders in the collection
   - Use double quotes to specify folder name. If you wish to run multiple folders, separate them with a comma (`,`)
   - Example: `--folder "QuickStart"` or `--folder "Health check,QuickStart"`
+- `--header` -- If you wish to add custom headers to the requests, you can pass them as a string
+  - Example: `--header "key:value"`
+  - If you want to pass multiple custom headers, you can pass multiple `--header` flags
+    - Example: `--header "key1:value1" --header "key2:value2"`
 - `--verbose` -- A boolean to print detailed logs (requests and responses)
 
 **Note:** Passing `--verbose` will also print the connector as well as admin API keys in the logs. So, make sure you don't push the commands with `--verbose` to any public repository.
diff --git a/crates/test_utils/src/main.rs b/crates/test_utils/src/main.rs
index 637122e468e..22c91e063d8 100644
--- a/crates/test_utils/src/main.rs
+++ b/crates/test_utils/src/main.rs
@@ -3,10 +3,10 @@ use std::process::{exit, Command};
 use test_utils::newman_runner;
 
 fn main() {
-    let mut newman_command: Command = newman_runner::command_generate();
+    let mut runner = newman_runner::generate_newman_command();
 
     // Execute the newman command
-    let output = newman_command.spawn();
+    let output = runner.newman_command.spawn();
     let mut child = match output {
         Ok(child) => child,
         Err(err) => {
@@ -16,6 +16,30 @@ fn main() {
     };
     let status = child.wait();
 
+    if runner.file_modified_flag {
+        let git_status = Command::new("git")
+            .args([
+                "restore",
+                format!("{}/event.prerequest.js", runner.collection_path).as_str(),
+            ])
+            .output();
+
+        match git_status {
+            Ok(output) => {
+                if output.status.success() {
+                    let stdout_str = String::from_utf8_lossy(&output.stdout);
+                    println!("Git command executed successfully: {stdout_str}");
+                } else {
+                    let stderr_str = String::from_utf8_lossy(&output.stderr);
+                    eprintln!("Git command failed with error: {stderr_str}");
+                }
+            }
+            Err(e) => {
+                eprintln!("Error running Git: {e}");
+            }
+        }
+    }
+
     let exit_code = match status {
         Ok(exit_status) => {
             if exit_status.success() {
diff --git a/crates/test_utils/src/newman_runner.rs b/crates/test_utils/src/newman_runner.rs
index c51556f8f25..af7fb559281 100644
--- a/crates/test_utils/src/newman_runner.rs
+++ b/crates/test_utils/src/newman_runner.rs
@@ -1,22 +1,34 @@
-use std::{env, process::Command};
+use std::{
+    env,
+    fs::OpenOptions,
+    io::{self, Write},
+    path::Path,
+    process::Command,
+};
 
 use clap::{arg, command, Parser};
 use masking::PeekInterface;
 
 use crate::connector_auth::{ConnectorAuthType, ConnectorAuthenticationMap};
-
 #[derive(Parser)]
 #[command(version, about = "Postman collection runner using newman!", long_about = None)]
 struct Args {
     /// Admin API Key of the environment
-    #[arg(short, long = "admin_api_key")]
+    #[arg(short, long)]
     admin_api_key: String,
     /// Base URL of the Hyperswitch environment
-    #[arg(short, long = "base_url")]
+    #[arg(short, long)]
     base_url: String,
     /// Name of the connector
-    #[arg(short, long = "connector_name")]
+    #[arg(short, long)]
     connector_name: String,
+    /// Custom headers
+    #[arg(short = 'H', long = "header")]
+    custom_headers: Option<Vec<String>>,
+    /// Minimum delay in milliseconds to be added before sending a request
+    /// By default, 7 milliseconds will be the delay
+    #[arg(short, long, default_value_t = 7)]
+    delay_request: u32,
     /// Folder name of specific tests
     #[arg(short, long = "folder")]
     folders: Option<String>,
@@ -25,6 +37,12 @@ struct Args {
     verbose: bool,
 }
 
+pub struct ReturnArgs {
+    pub newman_command: Command,
+    pub file_modified_flag: bool,
+    pub collection_path: String,
+}
+
 // Just by the name of the connector, this function generates the name of the collection dir
 // Example: CONNECTOR_NAME="stripe" -> OUTPUT: postman/collection-dir/stripe
 #[inline]
@@ -32,7 +50,29 @@ fn get_path(name: impl AsRef<str>) -> String {
     format!("postman/collection-dir/{}", name.as_ref())
 }
 
-pub fn command_generate() -> Command {
+// This function currently allows you to add only custom headers.
+// In future, as we scale, this can be modified based on the need
+fn insert_content<T, U>(dir: T, content_to_insert: U) -> io::Result<()>
+where
+    T: AsRef<Path> + std::fmt::Debug,
+    U: AsRef<str> + std::fmt::Debug,
+{
+    let file_name = "event.prerequest.js";
+    let file_path = dir.as_ref().join(file_name);
+
+    // Open the file in write mode or create it if it doesn't exist
+    let mut file = OpenOptions::new()
+        .write(true)
+        .append(true)
+        .create(true)
+        .open(file_path)?;
+
+    write!(file, "\n{:#?}", content_to_insert)?;
+
+    Ok(())
+}
+
+pub fn generate_newman_command() -> ReturnArgs {
     let args = Args::parse();
 
     let connector_name = args.connector_name;
@@ -129,7 +169,10 @@ pub fn command_generate() -> Command {
         ]);
     }
 
-    newman_command.arg("--delay-request").arg("7"); // 7 milli seconds delay
+    newman_command.args([
+        "--delay-request",
+        format!("{}", &args.delay_request).as_str(),
+    ]);
 
     newman_command.arg("--color").arg("on");
 
@@ -151,5 +194,24 @@ pub fn command_generate() -> Command {
         newman_command.arg("--verbose");
     }
 
-    newman_command
+    let mut modified = false;
+    if let Some(headers) = &args.custom_headers {
+        for header in headers {
+            if let Some((key, value)) = header.split_once(':') {
+                let content_to_insert =
+                    format!(r#"pm.request.headers.add({{key: "{key}", value: "{value}"}});"#);
+                if insert_content(&collection_path, &content_to_insert).is_ok() {
+                    modified = true;
+                }
+            } else {
+                eprintln!("Invalid header format: {}", header);
+            }
+        }
+    }
+
+    ReturnArgs {
+        newman_command,
+        file_modified_flag: modified,
+        collection_path,
+    }
 }

## Type of Change
<!-- Put an `x` in the boxes that apply -->

- [x] New feature
- [x] Documentation
- [x] CI/CD

## Description
<!-- Describe your changes in detail -->
Closes #2646 
This PR as usual, narrows the main aim of rustman by making specific to Hyperswitch. With this, you should now be able to:
- Set delay between each request sent as per your own wish while default being `7` milliseconds
- Pass in custom-headers of your wish in run time

### How custom-headers works

Custom headers inject the headers that you pass in command line into the `event.prerequest.js` file and sets a flag to `true`. After the collection is run, it checks the flag and depending on that, it will `git checkout HEAD -- <collection_name>/event.prerequest.js` to restore the file to same as before.  
We can also pass `-e environment.json` where you pass in custom headers but that requires some significant changes to be done to the collections to support that.

## Motivation and Context
<!--
Why is this change required? What problem does it solve?
If it fixes an open issue, please link to the issue here.

If you don't have an issue, we'd recommend starting with one first so the PR
can focus on the implementation (unless it is an obvious bug or documentation fix
that will have little conversation).
-->
This will allow us to pass different headers in run-time to test new things and also allow you to set delay of your wish. You can now set the delay to `100000000000` and wait for ages to run a single test :D

## How did you test it?
<!--
Did you write an integration/unit/API test to verify the code changes?
Or did you test this change manually (provide relevant screenshots)?
-->
Command used: (Custom headers taken from #2116) 
Delay set to 0.5 seconds
```sh
cargo run --bin test_utils -- --base_url=http://127.0.0.1:8080 --admin_api_key=test_admin --connector_name=stripe --folder "QuickStart" --header "payment_confirm_source:merchant_server" --header "another_header_key:and_its_value" --delay_request 4294967295
```
In run-time, we can see the custom-headers being injected here:
![image](https://github.com/juspay/hyperswitch/assets/69745008/f0b35bb5-3e5d-4731-906b-d4d38c27090a)

![image](https://github.com/juspay/hyperswitch/assets/69745008/d07633f4-b5f8-499a-bb3f-efe16d41dbde)
Value being stored in DB:
![image](https://github.com/juspay/hyperswitch/assets/69745008/d071621c-2723-4513-8567-913982e94749)

Ran Stripe Collection:
<img width="511" alt="image" src="https://github.com/juspay/hyperswitch/assets/69745008/aaefe649-f782-47a9-9166-d6bec893c52c">


## Checklist
<!-- Put an `x` in the boxes that apply -->

- [x] I formatted the code `cargo +nightly fmt --all`
- [x] I addressed lints thrown by `cargo clippy`
- [x] I reviewed the submitted code
