strest 0.1.10

Blazing-fast async HTTP load tester in Rust - lock-free design, real-time stats, distributed runs, and optional chart exports for high-load API testing.
Documentation
#![cfg(feature = "wasm")]

mod support_single;

use std::fmt::Write as _;
use std::io::Write;

use support_single::{run_strest, spawn_http_server_or_skip};
use tempfile::NamedTempFile;

fn escape_wat_string(input: &str) -> String {
    let mut out = String::with_capacity(input.len());
    for ch in input.chars() {
        match ch {
            '"' => out.push_str("\\\""),
            '\\' => out.push_str("\\\\"),
            '\n' => out.push_str("\\n"),
            '\r' => out.push_str("\\r"),
            '\t' => out.push_str("\\t"),
            ch if ch.is_control() => {
                if write!(out, "\\{:02x}", ch as u32).is_err() {
                    out.push_str("\\00");
                }
            }
            _ => out.push(ch),
        }
    }
    out
}

fn build_wasm_with_json(json: &str) -> Result<Vec<u8>, String> {
    let escaped = escape_wat_string(json);
    let len = i32::try_from(json.len()).map_err(|err| err.to_string())?;

    let wat = format!(
        "(module\n  (memory (export \"memory\") 1 2)\n  \
         (data (i32.const 0) \"{escaped}\")\n  \
         (func (export \"scenario_ptr\") (result i32) (i32.const 0))\n  \
         (func (export \"scenario_len\") (result i32) (i32.const {len}))\n)"
    );

    wat::parse_str(&wat).map_err(|err| err.to_string())
}

#[test]
fn e2e_wasm_script_runs() -> Result<(), String> {
    let Some((url, _server)) = spawn_http_server_or_skip()? else {
        return Ok(());
    };
    let json = format!(r#"{{"schema_version":1,"base_url":"{url}","steps":[{{"path":"/"}}]}}"#);
    let wasm_bytes = build_wasm_with_json(&json)?;

    let mut file = NamedTempFile::new().map_err(|err| err.to_string())?;
    file.write_all(&wasm_bytes).map_err(|err| err.to_string())?;

    let path = file
        .path()
        .to_str()
        .ok_or_else(|| "Temp file path is not UTF-8.".to_owned())?;

    let args = vec![
        "--script".to_owned(),
        path.to_owned(),
        "-t".to_owned(),
        "1".to_owned(),
        "--no-tui".to_owned(),
        "--summary".to_owned(),
        "--no-charts".to_owned(),
        "--max-tasks".to_owned(),
        "1".to_owned(),
    ];

    let output = run_strest(args)?;
    if !output.status.success() {
        return Err(format!(
            "stdout: {}\nstderr: {}",
            String::from_utf8_lossy(&output.stdout),
            String::from_utf8_lossy(&output.stderr)
        ));
    }

    Ok(())
}