formulate 1.2.0

formulate is a standalone server that listens for web form data submissions.
#[cfg(test)]
use super::rocket;
use super::spam::{check_for_spam_allowlist, check_for_spam_blocklist};
use super::strings::{REJECT_MSG, REJECT_MSG_ALT, SUCCESS_MSG, VERSION, WELCOME_MSG};
use pretty_assertions::assert_eq;
use rocket::http::{ContentType, Status};
use rocket::local::blocking::{Client, LocalResponse};

const EMAIL_ENCODED: &str = "sender%40mytestdomainforemail.tests";
const EMAIL_NORMAL: &str = "sender@mytestdomainforemail.tests";
const EMAIL_SPAM: &str = "test@test.com";
const EMAIL_VALIDATION_MSG: &str =
    "Invalid email address provided. Please check email and try again.";
const FULLNAME: &str = "Dr.+No";
const MESSAGE: &str = "No+Mr.+Bond%2C+I+expect+you+...+to+die.";
const SPAM_MESSAGE: &str =
    "No+Mr.+Bond%2C+I+expect+you+...+to+die.+http://www.advanceleadgeneration.com";
const SPAM_MESSAGE_BLOCKLIST: &str =
    "No+Mr.+Bond%2C+I+expect+you+...+to+die.+http://www.casinom.com";
const SPAM_ALLOWLIST_FAIL: &str = "No+Mr.+Bond%2C+I+expect+you+...+to+die.+https://casinom.com/unsubscribe.aspx?d=advanceleadgeneration.domain";
const SPAM_ALLOWLIST_PASS: &str =
    "No+Mr.+Bond%2C+I+expect+you+...+to+die.+https://advanceleadgeneration.com";
const SUBJECT: &str = "Allow+me+to+retort";
const WEBSITE: &str = "ilikegold.com";

mod mail_variants {
    pub const EMAIL_MISSING_PARTS: (&str, &str) = ("testtest.com", "Missing domain or user");
    pub const EMAIL_UNBALANCED: (&str, &str) =
        ("Test User <testtest.com", "Unbalanced angle bracket");
    pub const EMAIL_INVALID_USER: (&str, &str) =
        ("(sender@mytestdomainforemail.tests", "Invalid email user");
    pub const EMAIL_INVALID_DOMAIN: (&str, &str) =
        ("sender@#mytestdomainforemail.tests", "Invalid email domain");
}

fn create_response<'a>(
    client: &'a Client,
    response_type: ContentType,
    response_body: &'a str,
) -> LocalResponse<'a> {
    client
        .post(rocket::uri!(super::submit))
        .header(response_type)
        .body(response_body)
        .dispatch()
}

fn create_form_body_spam(email: &str) -> [String; 4] {
    [
        format!("full_name={FULLNAME}&email={email}&subject={SUBJECT}&message={SPAM_MESSAGE}&from_site={WEBSITE}"),
        format!("full_name={FULLNAME}&email={email}&subject={SUBJECT}&message={SPAM_MESSAGE_BLOCKLIST}&from_site={WEBSITE}"),
        format!("full_name={FULLNAME}&email={email}&subject={SUBJECT}&message={SPAM_ALLOWLIST_FAIL}&from_site={WEBSITE}"),
        format!("full_name={FULLNAME}&email={email}&subject={SUBJECT}&message={SPAM_ALLOWLIST_PASS}&from_site={WEBSITE}")
    ]
}

fn create_json_body_spam(email: &str) -> [String; 4] {
    [
        format!("{{\"full_name\":\"{FULLNAME}\",\"email\":\"{email}\",\"subject\":\"{SUBJECT}\",\"message\":\"{SPAM_MESSAGE}\",\"from_site\":\"{WEBSITE}\"}}"),
        format!("{{\"full_name\":\"{FULLNAME}\",\"email\":\"{email}\",\"subject\":\"{SUBJECT}\",\"message\":\"{SPAM_MESSAGE_BLOCKLIST}\",\"from_site\":\"{WEBSITE}\"}}"),
        format!("{{\"full_name\":\"{FULLNAME}\",\"email\":\"{email}\",\"subject\":\"{SUBJECT}\",\"message\":\"{SPAM_ALLOWLIST_FAIL}\",\"from_site\":\"{WEBSITE}\"}}"),
        format!("{{\"full_name\":\"{FULLNAME}\",\"email\":\"{email}\",\"subject\":\"{SUBJECT}\",\"message\":\"{SPAM_ALLOWLIST_PASS}\",\"from_site\":\"{WEBSITE}\"}}"),
    ]
}

fn create_form_body(email: &str) -> [String; 7] {
    [
      format!("full_name={FULLNAME}&email={email}&subject={SUBJECT}&message={MESSAGE}&from_site={WEBSITE}"),
      format!("fullname={FULLNAME}&email={email}&subject={SUBJECT}&message={MESSAGE}&from_site={WEBSITE}"),
      format!("fullname={FULLNAME}&e-mail={email}&subject={SUBJECT}&message={MESSAGE}&from_site={WEBSITE}"),
      format!("fullname={FULLNAME}&email={email}&subject={SUBJECT}&message={MESSAGE}&from_site={WEBSITE}"),
      format!("fullname={FULLNAME}&email={email}&subject={SUBJECT}&message={MESSAGE}&location={WEBSITE}"),
      format!("fullname={FULLNAME}&email={email}&subject={SUBJECT}&message={MESSAGE}&website={WEBSITE}"),
      format!("fullname={FULLNAME}&e-mail={email}&subject={SUBJECT}&message={MESSAGE}&site={WEBSITE}")
    ]
}

fn create_form_redirect_body(email: &str) -> [String; 1] {
    [
      format!("full_name={FULLNAME}&email={email}&subject={SUBJECT}&message={MESSAGE}&from_site={WEBSITE}&redirect=true"),
    ]
}

fn create_json_body(email: &str) -> [String; 8] {
    [
      format!("{{\"full_name\":\"{FULLNAME}\",\"email\":\"{email}\",\"subject\":\"{SUBJECT}\",\"message\":\"{MESSAGE}\",\"from_site\":\"{WEBSITE}\"}}"),
      format!("{{\"fullname\":\"{FULLNAME}\",\"email\":\"{email}\",\"subject\":\"{SUBJECT}\",\"message\":\"{MESSAGE}\",\"from_site\":\"{WEBSITE}\"}}"),
      format!("{{\"fullName\":\"{FULLNAME}\",\"email\":\"{email}\",\"subject\":\"{SUBJECT}\",\"message\":\"{MESSAGE}\",\"from_site\":\"{WEBSITE}\"}}"),
      format!("{{\"full_name\":\"{FULLNAME}\",\"e-mail\":\"{email}\",\"subject\":\"{SUBJECT}\",\"message\":\"{MESSAGE}\",\"from_site\":\"{WEBSITE}\"}}"),
      format!("{{\"full_name\":\"{FULLNAME}\",\"email\":\"{email}\",\"subject\":\"{SUBJECT}\",\"message\":\"{MESSAGE}\",\"site\":\"{WEBSITE}\"}}"),
      format!("{{\"full_name\":\"{FULLNAME}\",\"email\":\"{email}\",\"subject\":\"{SUBJECT}\",\"message\":\"{MESSAGE}\",\"website\":\"{WEBSITE}\"}}"),
      format!("{{\"full_name\":\"{FULLNAME}\",\"email\":\"{email}\",\"subject\":\"{SUBJECT}\",\"message\":\"{MESSAGE}\",\"location\":\"{WEBSITE}\"}}"),
      format!("{{\"full_name\":\"{FULLNAME}\",\"email\":\"{email}\",\"subject\":\"{SUBJECT}\",\"message\":\"{MESSAGE}\",\"from_site\":\"{WEBSITE}\"}}")
    ]
}

fn test_email_validity(validation_input: (&str, &str), client: &Client) {
    create_form_body(validation_input.0).map(|form_body| {
        let response = create_response(client, ContentType::Form, &form_body);
        assert_eq!(response.status(), Status::BadRequest);
        // assert_eq!(response.into_string().unwrap(), String::from(validation_input.1));
        assert_eq!(
            response.into_string().unwrap(),
            format!("email: {EMAIL_VALIDATION_MSG}")
        );
    });
}

fn test_email_validity_json(validation_input: (&str, &str), client: &Client) {
    create_json_body(validation_input.0).map(|form_body| {
        let response = create_response(client, ContentType::JSON, &form_body);
        assert_eq!(response.status(), Status::BadRequest);
        // println!("{}", response.into_string().unwrap());
        // assert_eq!(response.into_string().unwrap(), String::from(validation_input.1));
        assert_eq!(
            response.into_string().unwrap(),
            format!("email: {EMAIL_VALIDATION_MSG}")
        );
    });
}

#[test]
fn test_index() {
    let client = Client::tracked(rocket()).expect("Invalid rocket instance");

    let response = client.get(rocket::uri!(super::index)).dispatch();
    assert_eq!(response.status(), Status::Ok);
    assert_eq!(
        response.into_string(),
        Some(format!("{WELCOME_MSG}\nv{VERSION}"))
    );
}

#[test]
fn test_submit() {
    let client = Client::tracked(rocket()).expect("Invalid rocket instance");

    create_form_body(EMAIL_ENCODED).map(|form_body| {
        {
            let response = create_response(&client, ContentType::Form, &form_body);
            assert_eq!(response.into_string().unwrap(), SUCCESS_MSG);
        }
        let response = create_response(&client, ContentType::Form, &form_body);
        assert_eq!(response.status(), Status::Ok);
        let headers = response.headers().get("Location").next();
        assert_eq!(headers, None);
    });

    create_form_redirect_body(EMAIL_ENCODED).map(|form_body| {
        let response = create_response(&client, ContentType::Form, &form_body);
        assert_eq!(response.status(), Status::SeeOther);
        let headers = response.headers().get("Location").next();
        assert_eq!(headers, Some(WEBSITE));
    });

    test_email_validity(mail_variants::EMAIL_MISSING_PARTS, &client);
    test_email_validity(mail_variants::EMAIL_UNBALANCED, &client);
    test_email_validity(mail_variants::EMAIL_INVALID_USER, &client);
    test_email_validity(mail_variants::EMAIL_INVALID_DOMAIN, &client);
}

#[test]
fn test_submit_json() {
    let client = Client::tracked(rocket()).expect("Invalid rocket instance");

    create_json_body(EMAIL_NORMAL).map(|form_body| {
        {
            let response = create_response(&client, ContentType::JSON, &form_body);
            assert_eq!(response.into_string().unwrap(), SUCCESS_MSG);
        }
        let response = create_response(&client, ContentType::JSON, &form_body);
        assert_eq!(response.status(), Status::Ok);
    });

    test_email_validity_json(mail_variants::EMAIL_MISSING_PARTS, &client);
    test_email_validity_json(mail_variants::EMAIL_UNBALANCED, &client);
    test_email_validity_json(mail_variants::EMAIL_INVALID_USER, &client);
    test_email_validity_json(mail_variants::EMAIL_INVALID_DOMAIN, &client);
}

#[test]
/// This test isn't really useful unless you have a particular Rocket.toml config (which I do)
fn test_spam_config() {
    let client = Client::tracked(rocket()).expect("Invalid rocket instance");

    create_form_body_spam(EMAIL_ENCODED).map(|form_body| {
        if check_for_spam_blocklist(&form_body, REJECT_MSG) != Ok(()) {
            let response = create_response(&client, ContentType::Form, &form_body);
            assert_eq!(response.status(), Status::BadRequest);
            assert_eq!(response.into_string().unwrap(), REJECT_MSG);
        }
    });

    create_json_body_spam(EMAIL_NORMAL).map(|form_body| {
        if check_for_spam_blocklist(&form_body, REJECT_MSG) != Ok(()) {
            let response = create_response(&client, ContentType::JSON, &form_body);
            assert_eq!(response.status(), Status::BadRequest);
            assert_eq!(response.into_string().unwrap(), REJECT_MSG);
        }
    });
}

#[test]
#[ignore]
fn test_empty_config() {
    let client = Client::tracked(rocket()).expect("Invalid rocket instance");

    // Setting these variables interferes with the other tests when run in parallel
    // so we skip it here.
    std::env::set_var("FORMULATE_SENDING_EMAIL", "");
    std::env::set_var("FORMULATE_DESTINATION_EMAIL", "");
    std::env::set_var("FORMULATE_SPAM_BLOCKLIST", "[]");
    std::env::set_var("FORMULATE_SPAM_ALLOWLIST", "[]");

    create_form_body(EMAIL_ENCODED).map(|form_body| {
        let response = create_response(&client, ContentType::Form, &form_body);
        assert_eq!(response.status(), Status::BadRequest);
    });

    create_json_body(EMAIL_NORMAL).map(|form_body| {
        let response = create_response(&client, ContentType::JSON, &form_body);
        assert_eq!(response.status(), Status::BadRequest);
    });
}

#[test]
#[ignore]
fn test_default_config() {
    let client = Client::tracked(rocket()).expect("Invalid rocket instance");

    std::env::set_var("FORMULATE_SENDING_EMAIL", "noreply@localserver");
    std::env::set_var("FORMULATE_DESTINATION_EMAIL", "test@example.com");
    std::env::set_var("FORMULATE_SPAM_BLOCKING", "true");
    std::env::set_var(
        "FORMULATE_SPAM_BLOCKLIST",
        "[advanceleadgeneration.com, casinom.com]",
    );

    create_form_body(EMAIL_ENCODED).map(|form_body| {
        {
            let response = create_response(&client, ContentType::Form, &form_body);
            assert_eq!(response.into_string().unwrap(), SUCCESS_MSG);
        }
        let response = create_response(&client, ContentType::Form, &form_body);
        assert_eq!(response.status(), Status::Ok);
        let headers = response.headers().get("Location").next();
        assert_eq!(headers, None);
    });

    create_form_redirect_body(EMAIL_ENCODED).map(|form_body| {
        let response = create_response(&client, ContentType::Form, &form_body);
        assert_eq!(response.status(), Status::SeeOther);
        let headers = response.headers().get("Location").next();
        assert_eq!(headers, Some(WEBSITE));
    });

    create_json_body(EMAIL_NORMAL).map(|form_body| {
        {
            let response = create_response(&client, ContentType::JSON, &form_body);
            assert_eq!(response.into_string().unwrap(), SUCCESS_MSG);
        }
        let response = create_response(&client, ContentType::JSON, &form_body);
        assert_eq!(response.status(), Status::Ok);
    });

    // Use spammy domains in body
    create_form_body_spam(EMAIL_ENCODED).map(|form_body| {
        if check_for_spam_blocklist(&form_body, REJECT_MSG) != Ok(()) {
            {
                let response = create_response(&client, ContentType::Form, &form_body);
                assert_eq!(response.into_string().unwrap(), REJECT_MSG);
            }
            let response = create_response(&client, ContentType::Form, &form_body);
            assert_eq!(response.status(), Status::BadRequest);
        }
    });
    create_json_body_spam(EMAIL_NORMAL).map(|form_body| {
        if check_for_spam_blocklist(&form_body, REJECT_MSG) != Ok(()) {
            {
                let response = create_response(&client, ContentType::JSON, &form_body);
                assert_eq!(response.into_string().unwrap(), REJECT_MSG);
            }
            let response = create_response(&client, ContentType::JSON, &form_body);
            assert_eq!(response.status(), Status::BadRequest);
            assert_eq!(response.into_string().unwrap(), REJECT_MSG);
        }
    });

    // Use spammy email address
    create_json_body(EMAIL_SPAM).map(|form_body| {
        {
            let response = create_response(&client, ContentType::JSON, &form_body);
            assert_eq!(response.into_string().unwrap(), REJECT_MSG);
        }
        let response = create_response(&client, ContentType::JSON, &form_body);
        assert_eq!(response.status(), Status::BadRequest);
    });
    create_form_body(EMAIL_SPAM).map(|form_body| {
        {
            let response = create_response(&client, ContentType::Form, &form_body);
            assert_eq!(response.into_string().unwrap(), REJECT_MSG);
        }
        let response = create_response(&client, ContentType::Form, &form_body);
        assert_eq!(response.status(), Status::BadRequest);
    });
}

#[test]
#[ignore]
fn test_allowlist_spam_config() {
    let client = Client::tracked(rocket()).expect("Invalid rocket instance");
    std::env::set_var("FORMULATE_SENDING_EMAIL", "noreply@localserver");
    std::env::set_var("FORMULATE_DESTINATION_EMAIL", "test@example.com");
    std::env::set_var("FORMULATE_SPAM_BLOCKLIST", "[]");
    std::env::set_var("FORMULATE_SPAM_ALLOWLIST", "[advanceleadgeneration.com]");

    // Use spammy domains in body
    for (i, form_body) in create_form_body_spam(EMAIL_ENCODED).iter().enumerate() {
        if check_for_spam_allowlist(form_body, REJECT_MSG_ALT) != Ok(()) {
            let response = create_response(&client, ContentType::Form, form_body);
            if i != 0 && i != 3 {
                assert_eq!(response.status(), Status::BadRequest);
                assert_eq!(response.into_string().unwrap(), REJECT_MSG_ALT);
            } else {
                assert_eq!(response.status(), Status::Ok);
                assert_eq!(response.into_string().unwrap(), SUCCESS_MSG);
            }
        }
    }
    for (i, form_body) in create_json_body_spam(EMAIL_NORMAL).iter().enumerate() {
        if check_for_spam_allowlist(form_body, REJECT_MSG_ALT) != Ok(()) {
            let response = create_response(&client, ContentType::JSON, form_body);
            if i != 0 && i != 3 {
                assert_eq!(response.status(), Status::BadRequest);
                assert_eq!(response.into_string().unwrap(), REJECT_MSG_ALT);
            } else {
                assert_eq!(response.status(), Status::Ok);
                assert_eq!(response.into_string().unwrap(), SUCCESS_MSG);
            }
        }
    }
}

// #[test]
// fn test_no_emailbuild() {}
// #[test]
// fn test_no_sendmail() {}