use httpmock::Method::GET;
use httpmock::{Mock, MockRef, MockServer};
use serial_test::serial;
mod common;
use goose::goose::GooseTaskSet;
use goose::prelude::*;
use goose::GooseConfiguration;
const INDEX_PATH: &str = "/";
const REDIRECT_PATH: &str = "/redirect";
const REDIRECT2_PATH: &str = "/redirect2";
const REDIRECT3_PATH: &str = "/redirect3";
const ABOUT_PATH: &str = "/about.php";
const INDEX_KEY: usize = 0;
const REDIRECT_KEY: usize = 1;
const REDIRECT_KEY2: usize = 2;
const REDIRECT_KEY3: usize = 3;
const ABOUT_KEY: usize = 4;
const SERVER1_INDEX_KEY: usize = 0;
const SERVER1_ABOUT_KEY: usize = 1;
const SERVER1_REDIRECT_KEY: usize = 2;
const SERVER2_INDEX_KEY: usize = 3;
const SERVER2_ABOUT_KEY: usize = 4;
const EXPECT_WORKERS: usize = 2;
const USERS: usize = 4;
#[derive(Clone)]
enum TestType {
Chain,
Domain,
Sticky,
}
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(())
}
pub async fn get_redirect(user: &GooseUser) -> GooseTaskResult {
let mut goose = user.get(REDIRECT_PATH).await?;
if let Ok(r) = goose.response {
match r.text().await {
Ok(html) => {
if !html.contains("about page") {
return user.set_failure(
"about page body wrong",
&mut goose.request,
None,
None,
);
}
}
Err(e) => {
return user.set_failure(
format!("unexpected error parsing about page: {}", e).as_str(),
&mut goose.request,
None,
None,
);
}
}
}
Ok(())
}
pub async fn get_domain_redirect(user: &GooseUser) -> GooseTaskResult {
let _goose = user.get(REDIRECT_PATH).await?;
Ok(())
}
fn setup_mock_server_endpoints<'a>(
test_type: &TestType,
server: &'a MockServer,
server2: Option<&'a MockServer>,
) -> Vec<MockRef<'a>> {
let mut endpoints: Vec<MockRef> = Vec::new();
match test_type {
TestType::Chain => {
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(REDIRECT_PATH)
.return_status(301)
.return_header("Location", REDIRECT2_PATH)
.create_on(&server),
);
endpoints.push(
Mock::new()
.expect_method(GET)
.expect_path(REDIRECT2_PATH)
.return_status(302)
.return_header("Location", REDIRECT3_PATH)
.create_on(&server),
);
endpoints.push(
Mock::new()
.expect_method(GET)
.expect_path(REDIRECT3_PATH)
.return_status(303)
.return_header("Location", ABOUT_PATH)
.create_on(&server),
);
endpoints.push(
Mock::new()
.expect_method(GET)
.expect_path(ABOUT_PATH)
.return_status(200)
.return_body("<HTML><BODY>about page</BODY></HTML>")
.create_on(&server),
);
}
TestType::Domain | TestType::Sticky => {
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)
.return_body("<HTML><BODY>about page</BODY></HTML>")
.create_on(&server),
);
endpoints.push(
Mock::new()
.expect_method(GET)
.expect_path(REDIRECT_PATH)
.return_status(301)
.return_header("Location", &server2.unwrap().url(INDEX_PATH))
.create_on(&server),
);
endpoints.push(
Mock::new()
.expect_method(GET)
.expect_path(INDEX_PATH)
.return_status(200)
.create_on(&server2.unwrap()),
);
endpoints.push(
Mock::new()
.expect_method(GET)
.expect_path(ABOUT_PATH)
.return_status(200)
.create_on(&server2.unwrap()),
);
}
}
endpoints
}
fn common_build_configuration(
server: &MockServer,
sticky: bool,
worker: Option<bool>,
manager: Option<usize>,
) -> GooseConfiguration {
if let Some(expect_workers) = manager {
if sticky {
common::build_configuration(
&server,
vec![
"--sticky-follow",
"--manager",
"--expect-workers",
&expect_workers.to_string(),
"--users",
&USERS.to_string(),
"--hatch-rate",
&USERS.to_string(),
],
)
} else {
common::build_configuration(
&server,
vec![
"--manager",
"--expect-workers",
&expect_workers.to_string(),
"--users",
&USERS.to_string(),
"--hatch-rate",
&USERS.to_string(),
],
)
}
} else if worker.is_some() {
common::build_configuration(&server, vec!["--worker"])
} else if sticky {
common::build_configuration(
&server,
vec![
"--sticky-follow",
"--users",
&USERS.to_string(),
"--hatch-rate",
&USERS.to_string(),
],
)
} else {
common::build_configuration(
&server,
vec![
"--users",
&USERS.to_string(),
"--hatch-rate",
&USERS.to_string(),
],
)
}
}
fn validate_redirect(test_type: &TestType, mock_endpoints: &[MockRef]) {
match test_type {
TestType::Chain => {
assert!(mock_endpoints[INDEX_KEY].times_called() > 0);
assert!(mock_endpoints[REDIRECT_KEY].times_called() > 0);
assert!(mock_endpoints[REDIRECT_KEY2].times_called() > 0);
assert!(mock_endpoints[REDIRECT_KEY3].times_called() > 0);
assert!(mock_endpoints[ABOUT_KEY].times_called() > 0);
assert!(
mock_endpoints[REDIRECT_KEY].times_called()
== mock_endpoints[REDIRECT_KEY2].times_called()
);
assert!(
mock_endpoints[REDIRECT_KEY].times_called()
== mock_endpoints[REDIRECT_KEY3].times_called()
);
assert!(
mock_endpoints[REDIRECT_KEY].times_called()
== mock_endpoints[ABOUT_KEY].times_called()
);
}
TestType::Domain => {
assert!(mock_endpoints[SERVER1_INDEX_KEY].times_called() > 0);
assert!(mock_endpoints[SERVER1_REDIRECT_KEY].times_called() > 0);
assert!(mock_endpoints[SERVER1_ABOUT_KEY].times_called() > 0);
assert!(mock_endpoints[SERVER2_INDEX_KEY].times_called() > 0);
assert!(mock_endpoints[SERVER2_ABOUT_KEY].times_called() == 0);
}
TestType::Sticky => {
assert!(mock_endpoints[SERVER1_REDIRECT_KEY].times_called() == USERS);
assert!(mock_endpoints[SERVER1_INDEX_KEY].times_called() == 0);
assert!(mock_endpoints[SERVER1_ABOUT_KEY].times_called() == 0);
assert!(mock_endpoints[SERVER2_INDEX_KEY].times_called() > 0);
assert!(mock_endpoints[SERVER2_ABOUT_KEY].times_called() > 0);
}
}
}
fn get_tasks(test_type: &TestType) -> GooseTaskSet {
match test_type {
TestType::Chain => {
taskset!("LoadTest")
.register_task(task!(get_index))
.register_task(task!(get_redirect))
}
TestType::Domain | TestType::Sticky => {
taskset!("LoadTest")
.register_task(task!(get_domain_redirect))
.register_task(task!(get_index))
.register_task(task!(get_about))
}
}
}
fn run_standalone_test(test_type: TestType) {
let server1 = MockServer::start();
let server2 = MockServer::start();
let mock_endpoints = setup_mock_server_endpoints(&test_type, &server1, Some(&server2));
let sticky = match test_type {
TestType::Sticky => true,
TestType::Chain | TestType::Domain => false,
};
let configuration = common_build_configuration(&server1, sticky, None, None);
common::run_load_test(
common::build_load_test(configuration, &get_tasks(&test_type), None, None),
None,
);
validate_redirect(&test_type, &mock_endpoints);
}
fn run_gaggle_test(test_type: TestType) {
let server1 = MockServer::start();
let server2 = MockServer::start();
let mock_endpoints = setup_mock_server_endpoints(&test_type, &server1, Some(&server2));
let sticky = match test_type {
TestType::Sticky => true,
TestType::Chain | TestType::Domain => false,
};
let worker_configuration = common_build_configuration(&server1, sticky, Some(true), None);
let worker_goose_attack =
common::build_load_test(worker_configuration, &get_tasks(&test_type), None, None);
let worker_handles = common::launch_gaggle_workers(worker_goose_attack, EXPECT_WORKERS);
let manager_configuration =
common_build_configuration(&server1, sticky, None, Some(EXPECT_WORKERS));
let manager_goose_attack =
common::build_load_test(manager_configuration, &get_tasks(&test_type), None, None);
common::run_load_test(manager_goose_attack, Some(worker_handles));
validate_redirect(&test_type, &mock_endpoints);
}
#[test]
fn test_redirect() {
run_standalone_test(TestType::Chain);
}
#[test]
#[cfg_attr(not(feature = "gaggle"), ignore)]
#[serial]
fn test_redirect_gaggle() {
run_gaggle_test(TestType::Chain);
}
#[test]
fn test_domain_redirect() {
run_standalone_test(TestType::Domain);
}
#[test]
#[cfg_attr(not(feature = "gaggle"), ignore)]
#[serial]
fn test_domain_redirect_gaggle() {
run_gaggle_test(TestType::Domain);
}
#[test]
fn test_sticky_domain_redirect() {
run_standalone_test(TestType::Sticky);
}
#[test]
#[cfg_attr(not(feature = "gaggle"), ignore)]
#[serial]
fn test_sticky_domain_redirect_gaggle() {
run_gaggle_test(TestType::Sticky);
}