use gumdrop::Options; use httpmock::{Method::GET, MockServer}; use goose::config::GooseConfiguration; use goose::prelude::*; // Paths used in load tests performed during these tests. const PATH: &str = "/one"; const HTML: &str = r#" Title 1234ABCD

Test text on the page.

"#; // Test transaction. pub async fn get_path_valid(user: &mut GooseUser) -> TransactionResult { let goose = user.get(PATH).await?; goose_eggs::validate_and_load_static_assets( user, goose, &goose_eggs::Validate::builder() .title("1234ABCD") .not_title("Example") .text("Test text") .text("") .not_text("") .header_value("foo", "bar") .not_header("bar") .build(), ) .await?; Ok(()) } // Build appropriate configuration for these tests. fn build_configuration(server: &MockServer) -> GooseConfiguration { // Declare server_url so its lifetime is sufficient when needed. let server_url = server.base_url(); // Common elements in all our tests. let configuration = vec![ "--users", "1", "--hatch-rate", "4", "--iterations", "1", "--host", &server_url, "--co-mitigation", "disabled", "--quiet", ]; // Parse these options to generate a GooseConfiguration. GooseConfiguration::parse_args_default(&configuration) .expect("failed to parse options and generate a configuration") } async fn run_load_test(server: &MockServer) -> GooseMetrics { // Run the Goose Attack. let goose_metrics = build_load_test( build_configuration(server), vec![scenario!("LoadTest").register_transaction(transaction!(get_path_valid))], None, None, ) .execute() .await .unwrap(); // Load test always launches 1 user and makes 1 request. assert!(goose_metrics.total_users == 1); // Provide debug if this fails. if goose_metrics.requests.len() != 1 { println!("EXPECTED ONE REQUEST: {:#?}", goose_metrics.requests); } assert!(goose_metrics.requests.len() == 1); goose_metrics } // Create a GooseAttack object from the configuration, Scenarios, and optional start and // stop Transactions. #[allow(dead_code)] pub fn build_load_test( configuration: GooseConfiguration, scenarios: Vec, start_transaction: Option<&Transaction>, stop_transaction: Option<&Transaction>, ) -> GooseAttack { // First set up the common base configuration. let mut goose = crate::GooseAttack::initialize_with_config(configuration).unwrap(); for scenario in scenarios { goose = goose.register_scenario(scenario.clone()); } if let Some(transaction) = start_transaction { goose = goose.test_start(transaction.clone()); } if let Some(transaction) = stop_transaction { goose = goose.test_stop(transaction.clone()); } goose } #[tokio::test] // Make a single request and validate everything. async fn test_valid() { // Start the mock server. let server = MockServer::start(); let mock_endpoint = // Set up PATH, store in vector at KEY_ONE. server.mock(|when, then| { when.method(GET).path(PATH); then.status(200) .header("foo", "bar") .body(HTML); }); let goose_metrics = run_load_test(&server).await; assert!(mock_endpoint.hits() == 1); // Provide debug if this fails. if !goose_metrics.errors.is_empty() { println!("UNEXPECTED ERRORS: {:#?}", goose_metrics.errors); } assert!(goose_metrics.errors.is_empty()); } #[tokio::test] // Make a single request and confirm detection of invalid status code. async fn test_invalid_status() { // Start the mock server. let server = MockServer::start(); let mock_endpoint = // Set up PATH, store in vector at KEY_ONE. server.mock(|when, then| { when.method(GET).path(PATH); then.status(404) .header("foo", "bar") .body(HTML); }); let goose_metrics = run_load_test(&server).await; assert!(mock_endpoint.hits() == 1); // Provide debug if this fails. if goose_metrics.errors.len() != 1 { println!("EXPECTED ONE ERRORS: {:#?}", goose_metrics.errors); } assert!(goose_metrics.errors.len() == 1); } #[tokio::test] // Make a single request and confirm detection of invalid header. async fn test_invalid_header() { // Start the mock server. let server = MockServer::start(); let mock_endpoint = // Set up PATH, store in vector at KEY_ONE. server.mock(|when, then| { when.method(GET).path(PATH); then.status(200) .header("bar", "foo") .body(HTML); }); let goose_metrics = run_load_test(&server).await; assert!(mock_endpoint.hits() == 1); // Provide debug if this fails. if goose_metrics.errors.len() != 1 { println!("EXPECTED ONE ERRORS: {:#?}", goose_metrics.errors); } assert!(goose_metrics.errors.len() == 1); } #[tokio::test] // Make a single request and confirm detection of invalid header value. async fn test_invalid_header_value() { // Start the mock server. let server = MockServer::start(); let mock_endpoint = // Set up PATH, store in vector at KEY_ONE. server.mock(|when, then| { when.method(GET).path(PATH); then.status(200) .header("foo", "invalid") .body(HTML); }); let goose_metrics = run_load_test(&server).await; assert!(mock_endpoint.hits() == 1); // Provide debug if this fails. if goose_metrics.errors.len() != 1 { println!("EXPECTED ONE ERRORS: {:#?}", goose_metrics.errors); } assert!(goose_metrics.errors.len() == 1); }