rust_rest_test 0.1.1

An executable that can be used to run "unit tests" on a rust api
use crate::json_test;
use crate::json_test::JsonExpectation;
use crate::parser::types::{RequestType, StatusCode};
use crate::static_vars::set_err_true;
use crate::tester::Tester;
use colored::Colorize;
use curl::easy::Easy;
use serde_json::Value;
use std::io::Read;
use std::str::from_utf8;

pub fn run(link: String, tester: Tester) {
    let full_url = full_url_or_panic(&link, &tester);
    let req_type = req_type_or_panic(&tester);
    let req_content = req_content_or_empty(&tester);
    let test_expectations = tester.response_tester.free_testers;

    let mut easy = Easy::new();
    easy.url(full_url.as_str()).unwrap();
    test_response(&mut easy, test_expectations);
    send_request(&mut easy, req_type, req_content);
    test_response_status(&mut easy, tester.response_tester.res_type);
}

fn run_testers(response: String, testers: &Vec<JsonExpectation>) -> Result<(), ()> {
    let json: Value = serde_json::from_str(response.as_str()).map_err(|_| {
        println!("The response is not a json object");
        ()
    })?;

    match json_test::check_all(&json, &testers) {
        Ok(()) => Ok(()),
        Err(errors) => {
            errors.iter().for_each(|e| {
                println!("{}", e);
            });
            Err(())
        }
    }
}

fn send_request(easy: &mut Easy, req_type: RequestType, data: String) {
    match req_type {
        RequestType::POST => send_post_request(easy, data),
        RequestType::GET => easy.perform().unwrap(),
        RequestType::PUT => {
            // TODO: Implement me
        }
        RequestType::DELETE => {
            // TODO: Implement me
        }
    }
}

fn send_post_request(easy: &mut Easy, data: String) {
    easy.post(true).unwrap();
    let mut transfer = easy.transfer();
    transfer
        .read_function(|buf| Ok(data.as_bytes().read(buf).unwrap_or(0)))
        .unwrap();
    transfer.perform().unwrap();
}

fn test_response(easy: &mut Easy, expectations: Vec<JsonExpectation>) {
    easy.write_function(move |data| {
        let response = from_utf8(data).unwrap().to_string();

        if run_testers(response, &expectations).is_err() {
            set_err_true();
        }

        Ok(data.len())
    })
    .unwrap();
}

fn test_response_status(easy: &mut Easy, code: Option<StatusCode>) {
    match code {
        Some(StatusCode::OK) => test_response_status_by_value(easy, 200),
        Some(StatusCode::BAD_REQUEST) => test_response_status_by_value(easy, 400),
        Some(StatusCode::NOT_FOUND) => test_response_status_by_value(easy, 404),
        None => (),
    }
}

fn test_response_status_by_value(easy: &mut Easy, code: u32) {
    if let Ok(easycode) = easy.response_code() {
        if easycode != code {
            let err_msg = format!("\tExpected status code:{}\n\tFound: {}", code, easycode).red();
            println!("{}", err_msg);
            set_err_true();
        }
    }
}

fn full_url_or_panic(link: &String, tester: &Tester) -> String {
    match &tester.request_info.url_postfix {
        None => panic!(
            "Test failed because of missing url_postfix. \n\n{:?}",
            tester
        ),
        Some(postfix) => format!("{}{}", link, postfix),
    }
}

fn req_type_or_panic(tester: &Tester) -> RequestType {
    match &tester.request_info.req_type {
        None => panic!(
            "Test failed because of missing request type. \n\n{:?}",
            tester
        ),
        Some(req_type) => req_type.clone(),
    }
}

fn req_content_or_empty(tester: &Tester) -> String {
    match &tester.request_info.content {
        None => "".to_string(),
        Some(content) => content.clone(),
    }
}