use httpmock::{Method::GET, MockRef, MockServer};
use serial_test::serial;
use tokio::time::{sleep, 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 = 4;
const USERS: usize = 18;
const RUN_TIME: usize = 3;
#[derive(Clone)]
enum TestType {
TaskSets,
Tasks,
}
pub async fn one_with_delay(user: &GooseUser) -> GooseTaskResult {
let _goose = user.get(ONE_PATH).await?;
sleep(Duration::from_secs(RUN_TIME as u64 + 1)).await;
Ok(())
}
pub async fn two_with_delay(user: &GooseUser) -> GooseTaskResult {
let _goose = user.get(TWO_PATH).await?;
sleep(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> {
vec![
server.mock(|when, then| {
when.method(GET).path(ONE_PATH);
then.status(200);
}),
server.mock(|when, then| {
when.method(GET).path(TWO_PATH);
then.status(200);
}),
server.mock(|when, then| {
when.method(GET).path(THREE_PATH);
then.status(200);
}),
server.mock(|when, then| {
when.method(GET).path(START_ONE_PATH);
then.status(200);
}),
server.mock(|when, then| {
when.method(GET).path(STOP_ONE_PATH);
then.status(200);
}),
]
}
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, scheduler: &GooseScheduler, mock_endpoints: &[MockRef]) {
mock_endpoints[START_ONE_KEY].assert_hits(1);
match test_type {
TestType::TaskSets => {
match scheduler {
GooseScheduler::RoundRobin => {
mock_endpoints[TWO_KEY].assert_hits(mock_endpoints[ONE_KEY].hits());
mock_endpoints[ONE_KEY].assert_hits(USERS / 2);
}
GooseScheduler::Serial => {
mock_endpoints[ONE_KEY].assert_hits(USERS);
mock_endpoints[TWO_KEY].assert_hits(0);
}
GooseScheduler::Random => {
assert!(
mock_endpoints[ONE_KEY].hits() + mock_endpoints[TWO_KEY].hits() == USERS
);
}
}
}
TestType::Tasks => {
match scheduler {
GooseScheduler::RoundRobin => {
mock_endpoints[ONE_KEY].assert_hits(0);
mock_endpoints[TWO_KEY].assert_hits(USERS);
mock_endpoints[THREE_KEY].assert_hits(USERS);
}
GooseScheduler::Serial => {
mock_endpoints[ONE_KEY].assert_hits(0);
mock_endpoints[TWO_KEY].assert_hits(USERS);
mock_endpoints[THREE_KEY].assert_hits(USERS * 2);
}
GooseScheduler::Random => {
assert!(
mock_endpoints[ONE_KEY].hits() + mock_endpoints[TWO_KEY].hits() == USERS
);
}
}
}
}
mock_endpoints[STOP_ONE_KEY].assert_hits(1);
}
fn get_tasksets() -> (GooseTaskSet, GooseTaskSet, GooseTask, GooseTask) {
(
taskset!("TaskSetOne")
.register_task(task!(one_with_delay))
.set_weight(USERS)
.unwrap(),
taskset!("TaskSetTwo")
.register_task(task!(two_with_delay))
.set_weight(USERS + 1)
.unwrap(),
task!(start_one),
task!(stop_one),
)
}
fn get_tasks() -> (GooseTaskSet, GooseTask, GooseTask) {
(
taskset!("TaskSet")
.register_task(task!(three).set_weight(USERS * 2).unwrap())
.register_task(task!(two_with_delay).set_weight(USERS).unwrap())
.register_task(task!(one_with_delay).set_weight(USERS).unwrap()),
task!(start_one),
task!(stop_one),
)
}
fn run_standalone_test(test_type: &TestType, scheduler: &GooseScheduler) {
let server = MockServer::start();
let mock_endpoints = setup_mock_server_endpoints(&server);
let configuration = common_build_configuration(&server, None, None);
let goose_attack;
match test_type {
TestType::TaskSets => {
let (taskset1, taskset2, start_task, stop_task) = get_tasksets();
goose_attack = crate::GooseAttack::initialize_with_config(configuration)
.unwrap()
.register_taskset(taskset1)
.register_taskset(taskset2)
.test_start(start_task)
.test_stop(stop_task)
.set_scheduler(scheduler.clone());
}
TestType::Tasks => {
let (taskset1, start_task, stop_task) = get_tasks();
goose_attack = crate::GooseAttack::initialize_with_config(configuration)
.unwrap()
.register_taskset(taskset1)
.test_start(start_task)
.test_stop(stop_task)
.set_scheduler(scheduler.clone());
}
}
common::run_load_test(goose_attack, None);
validate_test(test_type, &scheduler, &mock_endpoints);
}
fn run_gaggle_test(test_type: &TestType, scheduler: &GooseScheduler) {
let server = MockServer::start();
let mock_endpoints = setup_mock_server_endpoints(&server);
let worker_configuration = common_build_configuration(&server, Some(true), None);
let goose_attack;
match test_type {
TestType::TaskSets => {
let (taskset1, taskset2, start_task, stop_task) = get_tasksets();
goose_attack = crate::GooseAttack::initialize_with_config(worker_configuration)
.unwrap()
.register_taskset(taskset1)
.register_taskset(taskset2)
.test_start(start_task)
.test_stop(stop_task)
.set_scheduler(scheduler.clone());
}
TestType::Tasks => {
let (taskset1, start_task, stop_task) = get_tasks();
goose_attack = crate::GooseAttack::initialize_with_config(worker_configuration)
.unwrap()
.register_taskset(taskset1)
.test_start(start_task)
.test_stop(stop_task)
.set_scheduler(scheduler.clone());
}
}
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;
match test_type {
TestType::TaskSets => {
let (taskset1, taskset2, start_task, stop_task) = get_tasksets();
manager_goose_attack =
crate::GooseAttack::initialize_with_config(manager_configuration)
.unwrap()
.register_taskset(taskset1)
.register_taskset(taskset2)
.test_start(start_task)
.test_stop(stop_task)
.set_scheduler(scheduler.clone());
}
TestType::Tasks => {
let (taskset1, start_task, stop_task) = get_tasks();
manager_goose_attack =
crate::GooseAttack::initialize_with_config(manager_configuration)
.unwrap()
.register_taskset(taskset1)
.test_start(start_task)
.test_stop(stop_task)
.set_scheduler(scheduler.clone());
}
}
common::run_load_test(manager_goose_attack, Some(worker_handles));
validate_test(test_type, &scheduler, &mock_endpoints);
}
#[test]
fn test_round_robin_taskset() {
run_standalone_test(&TestType::TaskSets, &GooseScheduler::RoundRobin);
}
#[test]
#[cfg_attr(not(feature = "gaggle"), ignore)]
#[serial]
fn test_round_robin_taskset_gaggle() {
run_gaggle_test(&TestType::TaskSets, &GooseScheduler::RoundRobin);
}
#[test]
fn test_round_robin_task() {
run_standalone_test(&TestType::Tasks, &GooseScheduler::RoundRobin);
}
#[test]
#[cfg_attr(not(feature = "gaggle"), ignore)]
#[serial]
fn test_round_robin_task_gaggle() {
run_gaggle_test(&TestType::Tasks, &GooseScheduler::RoundRobin);
}
#[test]
fn test_serial_taskset() {
run_standalone_test(&TestType::TaskSets, &GooseScheduler::Serial);
}
#[test]
#[cfg_attr(not(feature = "gaggle"), ignore)]
#[serial]
fn test_serial_taskset_gaggle() {
run_gaggle_test(&TestType::TaskSets, &GooseScheduler::Serial);
}
#[test]
fn test_serial_tasks() {
run_standalone_test(&TestType::Tasks, &GooseScheduler::Serial);
}
#[test]
fn test_random_taskset() {
run_standalone_test(&TestType::TaskSets, &GooseScheduler::Random);
}
#[test]
#[cfg_attr(not(feature = "gaggle"), ignore)]
#[serial]
fn test_random_taskset_gaggle() {
run_gaggle_test(&TestType::TaskSets, &GooseScheduler::Random);
}
#[test]
fn test_random_tasks() {
run_standalone_test(&TestType::Tasks, &GooseScheduler::Random);
}