use httpmock::Method::GET;
use httpmock::{Mock, MockRef, MockServer};
use serial_test::serial;
mod common;
use goose::prelude::*;
const INDEX_PATH: &str = "/";
const ABOUT_PATH: &str = "/about.html";
const INDEX_KEY: usize = 0;
const ABOUT_KEY: usize = 1;
const USERS: usize = 3;
const RUN_TIME: usize = 3;
const HATCH_RATE: &str = "10";
const LOG_LEVEL: usize = 0;
const METRICS_FILE: &str = "metrics-test.log";
const DEBUG_FILE: &str = "debug-test.log";
const LOG_FORMAT: &str = "raw";
const THROTTLE_REQUESTS: usize = 10;
const EXPECT_WORKERS: usize = 2;
pub async fn get_index(user: &GooseUser) -> GooseTaskResult {
let _goose = user.get(INDEX_PATH).await?;
Ok(())
}
pub async fn get_about(user: &GooseUser) -> GooseTaskResult {
let _goose = user.get(ABOUT_PATH).await?;
Ok(())
}
fn setup_mock_server_endpoints(server: &MockServer) -> Vec<MockRef> {
let mut endpoints: Vec<MockRef> = Vec::new();
endpoints.push(
Mock::new()
.expect_method(GET)
.expect_path(INDEX_PATH)
.return_status(200)
.create_on(&server),
);
endpoints.push(
Mock::new()
.expect_method(GET)
.expect_path(ABOUT_PATH)
.return_status(200)
.create_on(&server),
);
endpoints
}
fn validate_test(
goose_metrics: GooseMetrics,
mock_endpoints: &[MockRef],
metrics_files: &[String],
debug_files: &[String],
) {
assert!(mock_endpoints[INDEX_KEY].times_called() > 0);
assert!(mock_endpoints[ABOUT_KEY].times_called() > 0);
let index_metrics = goose_metrics
.requests
.get(&format!("GET {}", INDEX_PATH))
.unwrap();
let about_metrics = goose_metrics
.requests
.get(&format!("GET {}", ABOUT_PATH))
.unwrap();
assert!(index_metrics.response_time_counter == mock_endpoints[INDEX_KEY].times_called());
assert!(index_metrics.success_count == mock_endpoints[INDEX_KEY].times_called());
assert!(index_metrics.fail_count == 0);
assert!(about_metrics.response_time_counter == mock_endpoints[ABOUT_KEY].times_called());
assert!(about_metrics.success_count == mock_endpoints[ABOUT_KEY].times_called());
assert!(about_metrics.fail_count == 0);
assert!(!index_metrics.status_code_counts.is_empty());
assert!(!about_metrics.status_code_counts.is_empty());
assert!(goose_metrics.tasks.is_empty());
assert!(goose_metrics.users == USERS);
let mut metrics_lines = 0;
for metrics_file in metrics_files {
assert!(std::path::Path::new(metrics_file).exists());
metrics_lines += common::file_length(metrics_file);
}
assert!(
metrics_lines
== mock_endpoints[INDEX_KEY].times_called() + mock_endpoints[ABOUT_KEY].times_called()
);
for debug_file in debug_files {
assert!(std::path::Path::new(debug_file).exists());
assert!(common::file_length(debug_file) == 0);
}
assert!(goose_metrics.duration == RUN_TIME);
let number_of_processes = metrics_files.len();
assert!(metrics_lines <= (RUN_TIME + 1) * THROTTLE_REQUESTS * number_of_processes);
for file in metrics_files {
common::cleanup_files(vec![file]);
}
for file in debug_files {
common::cleanup_files(vec![file]);
}
}
#[test]
fn test_defaults() {
let metrics_file = "defaults-".to_string() + METRICS_FILE;
let debug_file = "defaults-".to_string() + DEBUG_FILE;
common::cleanup_files(vec![&metrics_file, &debug_file]);
let server = MockServer::start();
let mock_endpoints = setup_mock_server_endpoints(&server);
let mut config = common::build_configuration(&server, vec![]);
config.users = None;
config.run_time = "".to_string();
config.hatch_rate = None;
let host = std::mem::take(&mut config.host);
let goose_metrics = crate::GooseAttack::initialize_with_config(config)
.unwrap()
.register_taskset(taskset!("Index").register_task(task!(get_index)))
.register_taskset(taskset!("About").register_task(task!(get_about)))
.set_default(GooseDefault::Host, host.as_str())
.unwrap()
.set_default(GooseDefault::Users, USERS)
.unwrap()
.set_default(GooseDefault::RunTime, RUN_TIME)
.unwrap()
.set_default(GooseDefault::HatchRate, HATCH_RATE)
.unwrap()
.set_default(GooseDefault::LogLevel, LOG_LEVEL)
.unwrap()
.set_default(GooseDefault::MetricsFile, metrics_file.as_str())
.unwrap()
.set_default(GooseDefault::MetricsFormat, LOG_FORMAT)
.unwrap()
.set_default(GooseDefault::DebugFile, debug_file.as_str())
.unwrap()
.set_default(GooseDefault::DebugFormat, LOG_FORMAT)
.unwrap()
.set_default(GooseDefault::ThrottleRequests, THROTTLE_REQUESTS)
.unwrap()
.set_default(GooseDefault::StatusCodes, true)
.unwrap()
.set_default(GooseDefault::OnlySummary, true)
.unwrap()
.set_default(GooseDefault::NoTaskMetrics, true)
.unwrap()
.set_default(GooseDefault::NoResetMetrics, true)
.unwrap()
.set_default(GooseDefault::StickyFollow, true)
.unwrap()
.execute()
.unwrap();
validate_test(
goose_metrics,
&mock_endpoints,
&[metrics_file],
&[debug_file],
);
}
#[test]
#[cfg_attr(not(feature = "gaggle"), ignore)]
#[serial]
fn test_defaults_gaggle() {
let metrics_file = "gaggle-defaults".to_string() + METRICS_FILE;
let debug_file = "gaggle-defaults".to_string() + DEBUG_FILE;
for i in 0..EXPECT_WORKERS {
let file = metrics_file.to_string() + &i.to_string();
common::cleanup_files(vec![&file]);
let file = debug_file.to_string() + &i.to_string();
common::cleanup_files(vec![&file]);
}
let server = MockServer::start();
let mock_endpoints = setup_mock_server_endpoints(&server);
const HOST: &str = "127.0.0.1";
const PORT: usize = 9988;
let mut configuration = common::build_configuration(&server, vec![]);
configuration.users = None;
configuration.run_time = "".to_string();
configuration.hatch_rate = None;
let host = std::mem::take(&mut configuration.host);
let mut worker_handles = Vec::new();
for i in 0..EXPECT_WORKERS {
let worker_configuration = configuration.clone();
let worker_metrics_file = metrics_file.clone() + &i.to_string();
let worker_debug_file = debug_file.clone() + &i.to_string();
worker_handles.push(std::thread::spawn(move || {
let _ = crate::GooseAttack::initialize_with_config(worker_configuration)
.unwrap()
.register_taskset(taskset!("Index").register_task(task!(get_index)))
.register_taskset(taskset!("About").register_task(task!(get_about)))
.set_default(GooseDefault::ThrottleRequests, THROTTLE_REQUESTS)
.unwrap()
.set_default(GooseDefault::DebugFile, worker_debug_file.as_str())
.unwrap()
.set_default(GooseDefault::DebugFormat, LOG_FORMAT)
.unwrap()
.set_default(GooseDefault::MetricsFile, worker_metrics_file.as_str())
.unwrap()
.set_default(GooseDefault::MetricsFormat, LOG_FORMAT)
.unwrap()
.set_default(GooseDefault::Worker, true)
.unwrap()
.set_default(GooseDefault::ManagerHost, HOST)
.unwrap()
.set_default(GooseDefault::ManagerPort, PORT)
.unwrap()
.execute()
.unwrap();
}));
}
let goose_metrics = crate::GooseAttack::initialize_with_config(configuration)
.unwrap()
.register_taskset(taskset!("FooIndex").register_task(task!(get_index)))
.register_taskset(taskset!("About").register_task(task!(get_about)))
.set_default(GooseDefault::Host, host.as_str())
.unwrap()
.set_default(GooseDefault::Users, USERS)
.unwrap()
.set_default(GooseDefault::RunTime, RUN_TIME)
.unwrap()
.set_default(GooseDefault::HatchRate, HATCH_RATE)
.unwrap()
.set_default(GooseDefault::StatusCodes, true)
.unwrap()
.set_default(GooseDefault::OnlySummary, true)
.unwrap()
.set_default(GooseDefault::NoTaskMetrics, true)
.unwrap()
.set_default(GooseDefault::StickyFollow, true)
.unwrap()
.set_default(GooseDefault::Manager, true)
.unwrap()
.set_default(GooseDefault::ExpectWorkers, EXPECT_WORKERS)
.unwrap()
.set_default(GooseDefault::NoHashCheck, true)
.unwrap()
.set_default(GooseDefault::ManagerBindHost, HOST)
.unwrap()
.set_default(GooseDefault::ManagerBindPort, PORT)
.unwrap()
.execute()
.unwrap();
for worker_handle in worker_handles {
let _ = worker_handle.join();
}
let mut metrics_files: Vec<String> = vec![];
let mut debug_files: Vec<String> = vec![];
for i in 0..EXPECT_WORKERS {
let file = metrics_file.to_string() + &i.to_string();
metrics_files.push(file);
let file = debug_file.to_string() + &i.to_string();
debug_files.push(file);
}
validate_test(goose_metrics, &mock_endpoints, &metrics_files, &debug_files);
}
#[test]
fn test_no_defaults() {
let metrics_file = "nodefaults-".to_string() + METRICS_FILE;
let debug_file = "nodefaults-".to_string() + DEBUG_FILE;
common::cleanup_files(vec![&metrics_file, &debug_file]);
let server = MockServer::start();
let mock_endpoints = setup_mock_server_endpoints(&server);
let config = common::build_configuration(
&server,
vec![
"--users",
&USERS.to_string(),
"--hatch-rate",
&HATCH_RATE.to_string(),
"--run-time",
&RUN_TIME.to_string(),
"--metrics-file",
&metrics_file,
"--metrics-format",
LOG_FORMAT,
"--debug-file",
&debug_file,
"--debug-format",
LOG_FORMAT,
"--throttle-requests",
&THROTTLE_REQUESTS.to_string(),
"--no-reset-metrics",
"--no-task-metrics",
"--status-codes",
"--only-summary",
"--sticky-follow",
],
);
let goose_metrics = crate::GooseAttack::initialize_with_config(config)
.unwrap()
.register_taskset(taskset!("Index").register_task(task!(get_index)))
.register_taskset(taskset!("About").register_task(task!(get_about)))
.execute()
.unwrap();
validate_test(
goose_metrics,
&mock_endpoints,
&[metrics_file],
&[debug_file],
);
}
#[test]
#[cfg_attr(not(feature = "gaggle"), ignore)]
#[serial]
fn test_no_defaults_gaggle() {
let metrics_file = "gaggle-nodefaults".to_string() + METRICS_FILE;
let debug_file = "gaggle-nodefaults".to_string() + DEBUG_FILE;
for i in 0..EXPECT_WORKERS {
let file = metrics_file.to_string() + &i.to_string();
common::cleanup_files(vec![&file]);
let file = debug_file.to_string() + &i.to_string();
common::cleanup_files(vec![&file]);
}
let server = MockServer::start();
let mock_endpoints = setup_mock_server_endpoints(&server);
const HOST: &str = "127.0.0.1";
const PORT: usize = 9988;
let mut worker_handles = Vec::new();
for i in 0..EXPECT_WORKERS {
let worker_metrics_file = metrics_file.to_string() + &i.to_string();
let worker_debug_file = debug_file.to_string() + &i.to_string();
let worker_configuration = common::build_configuration(
&server,
vec![
"--worker",
"--manager-host",
&HOST.to_string(),
"--manager-port",
&PORT.to_string(),
"--metrics-file",
&worker_metrics_file,
"--metrics-format",
LOG_FORMAT,
"--debug-file",
&worker_debug_file,
"--debug-format",
LOG_FORMAT,
"--throttle-requests",
&THROTTLE_REQUESTS.to_string(),
"--verbose",
],
);
println!("{:#?}", worker_configuration);
worker_handles.push(std::thread::spawn(move || {
let _ = crate::GooseAttack::initialize_with_config(worker_configuration)
.unwrap()
.register_taskset(taskset!("Index").register_task(task!(get_index)))
.register_taskset(taskset!("About").register_task(task!(get_about)))
.execute()
.unwrap();
}));
}
let manager_configuration = common::build_configuration(
&server,
vec![
"--manager",
"--expect-workers",
&EXPECT_WORKERS.to_string(),
"--manager-bind-host",
&HOST.to_string(),
"--manager-bind-port",
&PORT.to_string(),
"--users",
&USERS.to_string(),
"--hatch-rate",
&HATCH_RATE.to_string(),
"--run-time",
&RUN_TIME.to_string(),
"--no-reset-metrics",
"--no-task-metrics",
"--status-codes",
"--only-summary",
"--sticky-follow",
"--verbose",
],
);
let goose_metrics = crate::GooseAttack::initialize_with_config(manager_configuration)
.unwrap()
.register_taskset(taskset!("Index").register_task(task!(get_index)))
.register_taskset(taskset!("About").register_task(task!(get_about)))
.execute()
.unwrap();
for worker_handle in worker_handles {
let _ = worker_handle.join();
}
let mut metrics_files: Vec<String> = vec![];
let mut debug_files: Vec<String> = vec![];
for i in 0..EXPECT_WORKERS {
let file = metrics_file.to_string() + &i.to_string();
metrics_files.push(file);
let file = debug_file.to_string() + &i.to_string();
debug_files.push(file);
}
validate_test(goose_metrics, &mock_endpoints, &metrics_files, &debug_files);
}
#[test]
fn test_defaults_no_metrics() {
let server = MockServer::start();
let mock_endpoints = setup_mock_server_endpoints(&server);
let mut config = common::build_configuration(&server, vec![]);
config.users = None;
config.run_time = "".to_string();
config.hatch_rate = None;
let goose_metrics = crate::GooseAttack::initialize_with_config(config)
.unwrap()
.register_taskset(taskset!("Index").register_task(task!(get_index)))
.register_taskset(taskset!("About").register_task(task!(get_about)))
.set_default(GooseDefault::Users, USERS)
.unwrap()
.set_default(GooseDefault::RunTime, RUN_TIME)
.unwrap()
.set_default(GooseDefault::HatchRate, HATCH_RATE)
.unwrap()
.set_default(GooseDefault::NoMetrics, true)
.unwrap()
.execute()
.unwrap();
assert!(mock_endpoints[INDEX_KEY].times_called() > 0);
assert!(mock_endpoints[ABOUT_KEY].times_called() > 0);
assert!(goose_metrics.requests.is_empty());
assert!(goose_metrics.tasks.is_empty());
assert!(goose_metrics.users == USERS);
assert!(goose_metrics.duration == RUN_TIME);
assert!(!goose_metrics.display_metrics);
assert!(!goose_metrics.display_status_codes);
}