use httpmock::Method::GET;
use httpmock::{Mock, MockRef, MockServer};
use serial_test::serial;
mod common;
use goose::goose::GooseMethod;
use goose::prelude::*;
use goose::GooseConfiguration;
const INDEX_PATH: &str = "/";
const ABOUT_PATH: &str = "/about.html";
const INDEX_KEY: usize = 0;
const ABOUT_KEY: usize = 1;
const EXPECT_WORKERS: usize = 2;
#[derive(Clone)]
enum TestType {
NoResetMetrics,
ResetMetrics,
}
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 common_build_configuration(server: &MockServer, custom: &mut Vec<&str>) -> GooseConfiguration {
let mut configuration = vec![
"--users",
"2",
"--hatch-rate",
"4",
"--run-time",
"2",
"--status-codes",
];
configuration.append(custom);
common::build_configuration(&server, configuration)
}
fn validate_one_taskset(
goose_metrics: &GooseMetrics,
mock_endpoints: &[MockRef],
configuration: &GooseConfiguration,
test_type: TestType,
) {
assert!(mock_endpoints[INDEX_KEY].times_called() > 0);
assert!(mock_endpoints[ABOUT_KEY].times_called() > 0);
let one_third_index = mock_endpoints[INDEX_KEY].times_called() / 3;
let difference = mock_endpoints[ABOUT_KEY].times_called() as i32 - one_third_index as i32;
assert!(difference >= -2 && difference <= 2);
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.path == INDEX_PATH);
assert!(index_metrics.method == GooseMethod::GET);
assert!(about_metrics.path == ABOUT_PATH);
assert!(about_metrics.method == GooseMethod::GET);
let status_code: u16 = 200;
match test_type {
TestType::ResetMetrics => {
println!(
"response_time_counter: {}, mock_endpoint_called: {}",
index_metrics.response_time_counter,
mock_endpoints[INDEX_KEY].times_called()
);
assert!(index_metrics.response_time_counter < mock_endpoints[INDEX_KEY].times_called());
assert!(
index_metrics.status_code_counts[&status_code]
< mock_endpoints[INDEX_KEY].times_called()
);
assert!(index_metrics.success_count < mock_endpoints[INDEX_KEY].times_called());
assert!(about_metrics.response_time_counter < mock_endpoints[ABOUT_KEY].times_called());
assert!(
about_metrics.status_code_counts[&status_code]
< mock_endpoints[ABOUT_KEY].times_called()
);
assert!(about_metrics.success_count < mock_endpoints[ABOUT_KEY].times_called());
}
TestType::NoResetMetrics => {
assert!(
index_metrics.response_time_counter == mock_endpoints[INDEX_KEY].times_called()
);
assert!(
index_metrics.status_code_counts[&status_code]
== mock_endpoints[INDEX_KEY].times_called()
);
assert!(index_metrics.success_count == mock_endpoints[INDEX_KEY].times_called());
assert!(
about_metrics.response_time_counter == mock_endpoints[ABOUT_KEY].times_called()
);
assert!(
about_metrics.status_code_counts[&status_code]
== mock_endpoints[ABOUT_KEY].times_called()
);
assert!(about_metrics.success_count == mock_endpoints[ABOUT_KEY].times_called());
}
}
assert!(index_metrics.fail_count == 0);
assert!(about_metrics.fail_count == 0);
assert!(goose_metrics.users == configuration.users.unwrap());
}
fn get_tasks() -> GooseTaskSet {
taskset!("LoadTest")
.register_task(task!(get_index).set_weight(9).unwrap())
.register_task(task!(get_about).set_weight(3).unwrap())
}
fn run_standalone_test(test_type: TestType) {
let server = MockServer::start();
let mock_endpoints = setup_mock_server_endpoints(&server);
let mut configuration_flags = match test_type {
TestType::NoResetMetrics => vec!["--no-reset-metrics"],
TestType::ResetMetrics => vec![],
};
let configuration = common_build_configuration(&server, &mut configuration_flags);
let goose_metrics = common::run_load_test(
common::build_load_test(configuration.clone(), &get_tasks(), None, None),
None,
);
validate_one_taskset(&goose_metrics, &mock_endpoints, &configuration, test_type);
}
fn run_gaggle_test(test_type: TestType) {
let server = MockServer::start();
let mock_endpoints = setup_mock_server_endpoints(&server);
let worker_configuration = common::build_configuration(&server, vec!["--worker"]);
let goose_attack = common::build_load_test(worker_configuration, &get_tasks(), None, None);
let worker_handles = common::launch_gaggle_workers(goose_attack, EXPECT_WORKERS);
let manager_configuration = match test_type {
TestType::NoResetMetrics => common_build_configuration(
&server,
&mut vec![
"--manager",
"--expect-workers",
&EXPECT_WORKERS.to_string(),
"--no-reset-metrics",
],
),
TestType::ResetMetrics => common_build_configuration(
&server,
&mut vec!["--manager", "--expect-workers", &EXPECT_WORKERS.to_string()],
),
};
let manager_goose_attack =
common::build_load_test(manager_configuration.clone(), &get_tasks(), None, None);
let goose_metrics = common::run_load_test(manager_goose_attack, Some(worker_handles));
validate_one_taskset(
&goose_metrics,
&mock_endpoints,
&manager_configuration,
test_type,
);
}
#[test]
fn test_one_taskset() {
run_standalone_test(TestType::NoResetMetrics);
}
#[test]
#[cfg_attr(not(feature = "gaggle"), ignore)]
#[serial]
fn test_one_taskset_gaggle() {
run_gaggle_test(TestType::NoResetMetrics);
}
#[test]
fn test_one_taskset_reset_metrics() {
run_standalone_test(TestType::ResetMetrics);
}