use httpmock::Method::GET;
use httpmock::{Mock, MockRef, MockServer};
use serial_test::serial;
use tokio::time::{delay_for, Duration};
mod common;
use goose::prelude::*;
use goose::GooseConfiguration;
const ONE_PATH: &str = "/one";
const TWO_PATH: &str = "/two";
const THREE_PATH: &str = "/three";
const START_ONE_PATH: &str = "/start/one";
const STOP_ONE_PATH: &str = "/stop/one";
const ONE_KEY: usize = 0;
const TWO_KEY: usize = 1;
const THREE_KEY: usize = 2;
const START_ONE_KEY: usize = 3;
const STOP_ONE_KEY: usize = 4;
const EXPECT_WORKERS: usize = 2;
const USERS: usize = 4;
const RUN_TIME: usize = 2;
#[derive(Clone)]
enum TestType {
NotSequenced,
Sequenced,
}
pub async fn one(user: &GooseUser) -> GooseTaskResult {
let _goose = user.get(ONE_PATH).await?;
Ok(())
}
pub async fn two_with_delay(user: &GooseUser) -> GooseTaskResult {
let _goose = user.get(TWO_PATH).await?;
delay_for(Duration::from_secs(RUN_TIME as u64 + 1)).await;
Ok(())
}
pub async fn three(user: &GooseUser) -> GooseTaskResult {
let _goose = user.get(THREE_PATH).await?;
Ok(())
}
pub async fn start_one(user: &GooseUser) -> GooseTaskResult {
let _goose = user.get(START_ONE_PATH).await?;
Ok(())
}
pub async fn stop_one(user: &GooseUser) -> GooseTaskResult {
let _goose = user.get(STOP_ONE_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(ONE_PATH)
.return_status(200)
.create_on(&server),
);
endpoints.push(
Mock::new()
.expect_method(GET)
.expect_path(TWO_PATH)
.return_status(200)
.create_on(&server),
);
endpoints.push(
Mock::new()
.expect_method(GET)
.expect_path(THREE_PATH)
.return_status(200)
.create_on(&server),
);
endpoints.push(
Mock::new()
.expect_method(GET)
.expect_path(START_ONE_PATH)
.return_status(200)
.create_on(&server),
);
endpoints.push(
Mock::new()
.expect_method(GET)
.expect_path(STOP_ONE_PATH)
.return_status(200)
.create_on(&server),
);
endpoints
}
fn common_build_configuration(
server: &MockServer,
worker: Option<bool>,
manager: Option<usize>,
) -> GooseConfiguration {
if let Some(expect_workers) = manager {
common::build_configuration(
&server,
vec![
"--manager",
"--expect-workers",
&expect_workers.to_string(),
"--users",
&USERS.to_string(),
"--hatch-rate",
&USERS.to_string(),
"--run-time",
&RUN_TIME.to_string(),
"--no-reset-metrics",
],
)
} else if worker.is_some() {
common::build_configuration(&server, vec!["--worker"])
} else {
common::build_configuration(
&server,
vec![
"--users",
&USERS.to_string(),
"--hatch-rate",
&USERS.to_string(),
"--run-time",
&RUN_TIME.to_string(),
"--no-reset-metrics",
],
)
}
}
fn validate_test(test_type: &TestType, mock_endpoints: &[MockRef]) {
assert!(mock_endpoints[START_ONE_KEY].times_called() == 1);
assert!(mock_endpoints[ONE_KEY].times_called() == USERS * 2);
match test_type {
TestType::NotSequenced => {
assert!(mock_endpoints[TWO_KEY].times_called() == USERS);
assert!(mock_endpoints[THREE_KEY].times_called() == USERS);
}
TestType::Sequenced => {
assert!(mock_endpoints[TWO_KEY].times_called() == USERS);
assert!(mock_endpoints[THREE_KEY].times_called() == 0);
}
}
assert!(mock_endpoints[STOP_ONE_KEY].times_called() == 1);
}
fn get_tasks(test_type: &TestType) -> (GooseTaskSet, GooseTask, GooseTask) {
match test_type {
TestType::NotSequenced => (
taskset!("LoadTest")
.register_task(task!(one).set_weight(2).unwrap())
.register_task(task!(three))
.register_task(task!(two_with_delay)),
task!(start_one),
task!(stop_one),
),
TestType::Sequenced => (
taskset!("LoadTest")
.register_task(task!(one).set_sequence(1).set_weight(2).unwrap())
.register_task(task!(three).set_sequence(3))
.register_task(task!(two_with_delay).set_sequence(2)),
task!(start_one),
task!(stop_one),
),
}
}
fn run_standalone_test(test_type: TestType) {
let server = MockServer::start();
let mock_endpoints = setup_mock_server_endpoints(&server);
let configuration = common_build_configuration(&server, None, None);
let (taskset, start_task, stop_task) = get_tasks(&test_type);
common::run_load_test(
common::build_load_test(configuration, &taskset, Some(&start_task), Some(&stop_task)),
None,
);
validate_test(&test_type, &mock_endpoints);
}
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, Some(true), None);
let (taskset, start_task, stop_task) = get_tasks(&test_type);
let goose_attack = common::build_load_test(
worker_configuration,
&taskset,
Some(&start_task),
Some(&stop_task),
);
let worker_handles = common::launch_gaggle_workers(goose_attack, EXPECT_WORKERS);
let manager_configuration = common_build_configuration(&server, None, Some(EXPECT_WORKERS));
let manager_goose_attack = common::build_load_test(
manager_configuration,
&taskset,
Some(&start_task),
Some(&stop_task),
);
common::run_load_test(manager_goose_attack, Some(worker_handles));
validate_test(&test_type, &mock_endpoints);
}
#[test]
fn test_not_sequenced() {
run_standalone_test(TestType::NotSequenced);
}
#[test]
#[cfg_attr(not(feature = "gaggle"), ignore)]
#[serial]
fn test_not_sequenced_gaggle() {
run_gaggle_test(TestType::NotSequenced);
}
#[test]
fn test_sequenced() {
run_standalone_test(TestType::Sequenced);
}
#[test]
#[cfg_attr(not(feature = "gaggle"), ignore)]
#[serial]
fn test_sequenced_gaggle() {
run_gaggle_test(TestType::Sequenced);
}