/* * Copyright (C) 2022 Aravinth Manivannan * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ use std::path::Path; use std::sync::Arc; use actix_web::{ body::{BoxBody, EitherBody}, dev::ServiceResponse, error::ResponseError, http::StatusCode, }; use mktemp::Temp; use serde::Serialize; use crate::ctx::api::v1::auth::{Login, Register}; use crate::ctx::Ctx; use crate::errors::*; use crate::page::Page; use crate::settings::Settings; use crate::*; pub async fn get_ctx() -> (Temp, Arc) { // mktemp::Temp is returned because the temp directory created // is removed once the variable goes out of scope let mut settings = Settings::new().unwrap(); let tmp_dir = Temp::new_dir().unwrap(); println!("[log] Test temp directory: {}", tmp_dir.to_str().unwrap()); let mut pages = Vec::with_capacity(settings.pages.len()); for page in settings.pages.iter() { let name = Path::new(&page.path).file_name().unwrap().to_str().unwrap(); let path = tmp_dir.as_path().join(name); let page = Page { path: path.to_str().unwrap().to_string(), secret: page.secret.clone(), branch: page.branch.clone(), repo: page.repo.clone(), domain: "mcaptcha.org".into(), }; pages.push(Arc::new(page)); } settings.pages = pages; settings.init(); println!("[log] Initialzing settings again with test config"); settings.init(); (tmp_dir, Ctx::new(settings).await) } #[allow(dead_code, clippy::upper_case_acronyms)] pub struct FORM; #[macro_export] macro_rules! post_request { ($uri:expr) => { actix_web::test::TestRequest::post().uri($uri) }; ($serializable:expr, $uri:expr) => { actix_web::test::TestRequest::post() .uri($uri) .insert_header((actix_web::http::header::CONTENT_TYPE, "application/json")) .set_payload(serde_json::to_string($serializable).unwrap()) }; ($serializable:expr, $uri:expr, FORM) => { actix_web::test::TestRequest::post() .uri($uri) .set_form($serializable) }; } #[macro_export] macro_rules! get_request { ($app:expr,$route:expr ) => { test::call_service(&$app, test::TestRequest::get().uri($route).to_request()).await }; ($app:expr, $route:expr, $cookies:expr) => { test::call_service( &$app, test::TestRequest::get() .uri($route) .cookie($cookies) .to_request(), ) .await }; } #[macro_export] macro_rules! delete_request { ($app:expr,$route:expr ) => { test::call_service(&$app, test::TestRequest::delete().uri($route).to_request()).await }; ($app:expr, $route:expr, $cookies:expr) => { test::call_service( &$app, test::TestRequest::delete() .uri($route) .cookie($cookies) .to_request(), ) .await }; } #[macro_export] macro_rules! get_app { ($ctx:expr) => { actix_web::test::init_service( actix_web::App::new() .app_data($crate::get_json_err()) .wrap($crate::get_identity_service(&$ctx.settings)) .wrap(actix_web::middleware::NormalizePath::new( actix_web::middleware::TrailingSlash::Trim, )) .configure($crate::routes::services) .app_data($crate::WebData::new($ctx.clone())), ) }; } /// Utility function to check for status of a test response, attempt response payload serialization /// and print payload if response status doesn't match expected status #[macro_export] macro_rules! check_status { ($resp:expr, $expected:expr) => { let status = $resp.status(); if status != $expected { eprintln!( "[error] Expected status code: {} received: {status}", $expected ); let response: serde_json::Value = actix_web::test::read_body_json($resp).await; eprintln!("[error] Body:\n{:#?}", response); assert_eq!(status, $expected); panic!() } { assert_eq!(status, $expected); } }; } #[macro_export] macro_rules! get_cookie { ($resp:expr) => { $resp.response().cookies().next().unwrap().to_owned() }; } impl Ctx { /// register and signin utility pub async fn register_and_signin( &self, name: &str, email: &str, password: &str, ) -> (Login, ServiceResponse>) { self.register_test(name, email, password).await; self.signin_test(name, password).await } pub fn to_arc(&self) -> Arc { Arc::new(self.clone()) } /// register utility pub async fn register_test(&self, name: &str, email: &str, password: &str) { let app = get_app!(self.to_arc()).await; // 1. Register let msg = Register { username: name.into(), password: password.into(), confirm_password: password.into(), email: email.into(), }; println!("{:?}", msg); let resp = actix_web::test::call_service( &app, post_request!(&msg, crate::V1_API_ROUTES.auth.register).to_request(), ) .await; // let resp_err: ErrorToResponse = actix_web::test::read_body_json(resp).await; // panic!("{}", resp_err.error); assert_eq!(resp.status(), StatusCode::OK); } /// signin util pub async fn signin_test( &self, name: &str, password: &str, ) -> (Login, ServiceResponse>) { let app = get_app!(self.to_arc()).await; // 2. signin let creds = Login { login: name.into(), password: password.into(), }; let signin_resp = actix_web::test::call_service( &app, post_request!(&creds, V1_API_ROUTES.auth.login).to_request(), ) .await; assert_eq!(signin_resp.status(), StatusCode::OK); (creds, signin_resp) } /// pub duplicate test pub async fn bad_post_req_test( &self, name: &str, password: &str, url: &str, payload: &T, err: ServiceError, ) { let (_, signin_resp) = self.signin_test(name, password).await; let cookies = get_cookie!(signin_resp); let app = get_app!(self.to_arc()).await; let resp = actix_web::test::call_service( &app, post_request!(&payload, url) .cookie(cookies.clone()) .to_request(), ) .await; assert_eq!(resp.status(), err.status_code()); let resp_err: ErrorToResponse = actix_web::test::read_body_json(resp).await; //println!("{}", txt.error); assert_eq!(resp_err.error, format!("{}", err)); } /// bad post req test without payload pub async fn bad_post_req_test_witout_payload( &self, name: &str, password: &str, url: &str, err: ServiceError, ) { let (_, signin_resp) = self.signin_test(name, password).await; let app = get_app!(self.to_arc()).await; let cookies = get_cookie!(signin_resp); let resp = actix_web::test::call_service( &app, post_request!(url).cookie(cookies.clone()).to_request(), ) .await; assert_eq!(resp.status(), err.status_code()); let resp_err: ErrorToResponse = actix_web::test::read_body_json(resp).await; //println!("{}", resp_err.error); assert_eq!(resp_err.error, format!("{}", err)); } }