Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(test_utils): Add custom-headers and custom delay support to rustman #2636

Merged
merged 19 commits into from
Nov 7, 2023
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/postman-collection-runner.yml
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ jobs:

for i in $(echo "$CONNECTORS" | tr "," "\n"); do
echo $i
if ! cargo run --bin test_utils -- --connector_name="$i" --base_url="$BASE_URL" --admin_api_key="$ADMIN_API_KEY"; then
if ! cargo run --bin test_utils -- --connector-name="$i" --base-url="$BASE_URL" --admin-api-key="$ADMIN_API_KEY"; then
failed_connectors+=("$i")
fi
done
Expand Down
7 changes: 7 additions & 0 deletions crates/test_utils/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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"`
pixincreate marked this conversation as resolved.
Show resolved Hide resolved
- `--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.
Expand Down
32 changes: 29 additions & 3 deletions crates/test_utils/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::command_generate();

// 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) => {
Expand All @@ -16,6 +16,32 @@ fn main() {
};
let status = child.wait();

if runner.file_modified_flag {
let git_status = Command::new("git")
.args([
"checkout",
"HEAD",
"--",
format!("{}/event.prerequest.js", runner.collection_path).as_str(),
pixincreate marked this conversation as resolved.
Show resolved Hide resolved
])
.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() {
Expand All @@ -27,7 +53,7 @@ fn main() {
}
}
Err(err) => {
eprintln!("Failed to wait for command execution: {err}");
eprintln!("Failed to wait for command execution: {}", err);
exit(1);
}
};
Expand Down
85 changes: 77 additions & 8 deletions crates/test_utils/src/newman_runner.rs
Original file line number Diff line number Diff line change
@@ -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(long = "header")]
custom_headers: Option<Vec<String>>,
/// Minimum delay in milliseconds to be added before sending a request
pixincreate marked this conversation as resolved.
Show resolved Hide resolved
/// 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>,
Expand All @@ -25,14 +37,42 @@ 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]
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 command_generate() -> ReturnArgs {
let args = Args::parse();
pixincreate marked this conversation as resolved.
Show resolved Hide resolved

let connector_name = args.connector_name;
Expand Down Expand Up @@ -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");

Expand All @@ -151,5 +194,31 @@ 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
.splitn(2, ':')
.collect::<Vec<_>>()
.as_slice()
.split_first()
{
pixincreate marked this conversation as resolved.
Show resolved Hide resolved
let content_to_insert = format!(
"pm.request.headers.add({{key: \"{}\", value: \"{:#?}\"}});",
key, value
);
if insert_content(&collection_path, &content_to_insert).is_ok() {
pixincreate marked this conversation as resolved.
Show resolved Hide resolved
modified = true;
}
} else {
eprintln!("Invalid header format: {}", header);
}
}
}

ReturnArgs {
newman_command,
file_modified_flag: modified,
collection_path,
}
}
Loading