pub mod dataset;
pub mod datafile;
pub mod metrics;
pub mod runner;
pub mod report;
pub mod substitute;
pub use dataset::Dataset;
pub use datafile::{DataFile, DataRow};
pub use metrics::PerfMetrics;
pub use runner::PerfRunner;
pub use report::PerfReport;
pub use substitute::{extract_placeholders, get_row_for_request, substitute, validate_template};
#[cfg(test)]
mod integration_tests {
use std::io::Write;
use super::{DataFile, get_row_for_request, substitute, validate_template};
fn write_temp(filename: &str, content: &str) -> std::path::PathBuf {
let path = std::env::temp_dir().join(filename);
let mut f = std::fs::File::create(&path).expect("create temp file");
write!(f, "{}", content).unwrap();
path
}
#[test]
fn test_data_file_standalone_substitution() {
let csv = "user_id,token\nu1,t1\nu2,t2\nu3,t3";
let path = write_temp("hurley_integ_standalone.csv", csv);
let df = DataFile::from_path(&path).expect("load CSV");
let _ = std::fs::remove_file(&path);
let url_tmpl = "https://api.example.com/users/{{user_id}}";
let hdr_tmpl = "Authorization: Bearer {{token}}";
let body_tmpl = r#"{"id": "{{user_id}}"}"#;
for (i, (uid, tok)) in [("u1", "t1"), ("u2", "t2"), ("u3", "t3")]
.iter()
.enumerate()
{
let row = get_row_for_request(&df, i);
let url = substitute(url_tmpl, row).unwrap();
assert!(
url.contains(uid),
"row {i}: URL should contain '{uid}' but got '{url}'"
);
let hdr = substitute(hdr_tmpl, row).unwrap();
assert!(
hdr.contains(tok),
"row {i}: header should contain '{tok}' but got '{hdr}'"
);
let body = substitute(body_tmpl, row).unwrap();
assert!(
body.contains(uid),
"row {i}: body should contain '{uid}' but got '{body}'"
);
}
}
#[test]
fn test_data_file_cycling_9_requests_3_rows() {
let csv = "user_id\nrow0\nrow1\nrow2";
let path = write_temp("hurley_integ_cycle.csv", csv);
let df = DataFile::from_path(&path).expect("load CSV");
let _ = std::fs::remove_file(&path);
let url_tmpl = "https://api.example.com/users/{{user_id}}";
let expected_row = |i: usize| -> &'static str {
match i % 3 {
0 => "row0",
1 => "row1",
_ => "row2",
}
};
for i in 0..9usize {
let row = get_row_for_request(&df, i);
let url = substitute(url_tmpl, row).unwrap();
let exp = expected_row(i);
assert!(
url.contains(exp),
"request {i}: expected '{exp}' but URL was '{url}'"
);
}
}
#[test]
fn test_validate_template_fails_before_execution() {
let csv = "user_id,token\nu1,t1";
let path = write_temp("hurley_integ_validate.csv", csv);
let df = DataFile::from_path(&path).expect("load CSV");
let _ = std::fs::remove_file(&path);
let template = "{{user_id}}/{{missing_col}}";
let result = validate_template(template, df.columns());
assert!(result.is_err(), "expected Err for missing column");
let msg = result.unwrap_err().to_string();
assert!(
msg.contains("missing_col"),
"error should name the missing column; got: {msg}"
);
}
#[test]
fn test_data_file_json_format() {
let json = r#"[{"id": "1", "name": "alice"}, {"id": "2", "name": "bob"}]"#;
let path = write_temp("hurley_integ_json.json", json);
let df = DataFile::from_path(&path).expect("load JSON");
let _ = std::fs::remove_file(&path);
assert_eq!(df.len(), 2, "expected 2 rows in JSON data file");
let tmpl = "Hello {{name}} #{{id}}";
let row0 = get_row_for_request(&df, 0);
let out0 = substitute(tmpl, row0).unwrap();
assert_eq!(out0, "Hello alice #1", "row 0 substitution mismatch");
let row1 = get_row_for_request(&df, 1);
let out1 = substitute(tmpl, row1).unwrap();
assert_eq!(out1, "Hello bob #2", "row 1 substitution mismatch");
}
#[test]
fn test_backward_compat_no_data_file() {
let data_file: Option<&DataFile> = None;
let url = "https://api.example.com/static/resource";
let effective_url = if let Some(df) = data_file {
let row = get_row_for_request(df, 0);
substitute(url, row).unwrap()
} else {
url.to_string()
};
assert_eq!(effective_url, url, "without data file, URL must be unchanged");
let url_with_placeholders = "https://api.example.com/users/{{user_id}}";
let effective = if let Some(df) = data_file {
let row = get_row_for_request(df, 0);
substitute(url_with_placeholders, row).unwrap()
} else {
url_with_placeholders.to_string()
};
assert_eq!(
effective, url_with_placeholders,
"without data file, template string must be passed through unchanged"
);
}
}