feat: test helper, snapshot testing
This commit is contained in:
parent
ed7ce14990
commit
931b7e159d
43
Cargo.lock
generated
43
Cargo.lock
generated
@ -748,6 +748,7 @@ dependencies = [
|
|||||||
"dotenv",
|
"dotenv",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"idna 1.0.2",
|
"idna 1.0.2",
|
||||||
|
"insta",
|
||||||
"lettre",
|
"lettre",
|
||||||
"pico-args",
|
"pico-args",
|
||||||
"quick-xml",
|
"quick-xml",
|
||||||
@ -886,6 +887,18 @@ dependencies = [
|
|||||||
"crossbeam-utils",
|
"crossbeam-utils",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "console"
|
||||||
|
version = "0.15.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb"
|
||||||
|
dependencies = [
|
||||||
|
"encode_unicode",
|
||||||
|
"lazy_static",
|
||||||
|
"libc",
|
||||||
|
"windows-sys 0.52.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "const-oid"
|
name = "const-oid"
|
||||||
version = "0.9.6"
|
version = "0.9.6"
|
||||||
@ -1180,6 +1193,12 @@ version = "0.2.9"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e079f19b08ca6239f47f8ba8509c11cf3ea30095831f7fed61441475edd8c449"
|
checksum = "e079f19b08ca6239f47f8ba8509c11cf3ea30095831f7fed61441475edd8c449"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "encode_unicode"
|
||||||
|
version = "0.3.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "encoding_rs"
|
name = "encoding_rs"
|
||||||
version = "0.8.35"
|
version = "0.8.35"
|
||||||
@ -1825,6 +1844,18 @@ dependencies = [
|
|||||||
"generic-array",
|
"generic-array",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "insta"
|
||||||
|
version = "1.41.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7e9ffc4d4892617c50a928c52b2961cb5174b6fc6ebf252b2fac9d21955c48b8"
|
||||||
|
dependencies = [
|
||||||
|
"console",
|
||||||
|
"lazy_static",
|
||||||
|
"linked-hash-map",
|
||||||
|
"similar",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "instant"
|
name = "instant"
|
||||||
version = "0.1.13"
|
version = "0.1.13"
|
||||||
@ -1951,6 +1982,12 @@ dependencies = [
|
|||||||
"vcpkg",
|
"vcpkg",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "linked-hash-map"
|
||||||
|
version = "0.5.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "linux-raw-sys"
|
name = "linux-raw-sys"
|
||||||
version = "0.3.8"
|
version = "0.3.8"
|
||||||
@ -2774,6 +2811,12 @@ dependencies = [
|
|||||||
"rand_core",
|
"rand_core",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "similar"
|
||||||
|
version = "2.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1de1d4f81173b03af4c0cbed3c898f6bff5b870e4a7f5d6f4057d62a7a4b686e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "slab"
|
name = "slab"
|
||||||
version = "0.4.9"
|
version = "0.4.9"
|
||||||
|
@ -41,6 +41,9 @@ actix-http = "3.9.0"
|
|||||||
built = "0.7.4"
|
built = "0.7.4"
|
||||||
static-files = "0.2.1"
|
static-files = "0.2.1"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
insta = "1.41.1"
|
||||||
|
|
||||||
# [dev-dependencies]
|
# [dev-dependencies]
|
||||||
# brass-web = { path = "." }
|
# brass-web = { path = "." }
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use actix_web::{web, HttpResponse, Responder};
|
use actix_web::{web, HttpResponse, Responder};
|
||||||
|
use brass_macros::db_test;
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -7,6 +8,11 @@ use crate::{
|
|||||||
utils::ApplicationError,
|
utils::ApplicationError,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
use crate::utils::test_helper::{test_delete, DbTestContext, RequestConfig};
|
||||||
|
#[cfg(test)]
|
||||||
|
use actix_http::StatusCode;
|
||||||
|
|
||||||
#[actix_web::delete("/area/delete/{id}")]
|
#[actix_web::delete("/area/delete/{id}")]
|
||||||
pub async fn delete(
|
pub async fn delete(
|
||||||
user: web::ReqData<User>,
|
user: web::ReqData<User>,
|
||||||
@ -25,3 +31,49 @@ pub async fn delete(
|
|||||||
|
|
||||||
return Ok(HttpResponse::Ok().finish());
|
return Ok(HttpResponse::Ok().finish());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[db_test]
|
||||||
|
async fn works_when_user_is_admin(context: &DbTestContext) {
|
||||||
|
Area::create(&context.db_pool, "Area to delete")
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert!(Area::read_by_id(&context.db_pool, 2)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.is_some());
|
||||||
|
|
||||||
|
let app = context.app().await;
|
||||||
|
let config = RequestConfig {
|
||||||
|
uri: "/area/delete/2".to_string(),
|
||||||
|
role: Role::Admin,
|
||||||
|
function: crate::models::Function::Posten,
|
||||||
|
user_area: 1,
|
||||||
|
};
|
||||||
|
let response = test_delete(&context.db_pool, app, &config).await;
|
||||||
|
|
||||||
|
assert_eq!(StatusCode::OK, response.status());
|
||||||
|
assert!(Area::read_by_id(&context.db_pool, 2)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.is_none());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[db_test]
|
||||||
|
async fn does_not_work_when_user_is_not_admin(context: &DbTestContext) {
|
||||||
|
Area::create(&context.db_pool, "Area to delete")
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert!(Area::read_by_id(&context.db_pool, 2)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.is_some());
|
||||||
|
|
||||||
|
let app = context.app().await;
|
||||||
|
let response = test_delete(&context.db_pool, app, &RequestConfig::new("/area/delete/2")).await;
|
||||||
|
|
||||||
|
assert_eq!(StatusCode::UNAUTHORIZED, response.status());
|
||||||
|
assert!(Area::read_by_id(&context.db_pool, 2)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.is_some());
|
||||||
|
}
|
||||||
|
@ -1,7 +1,18 @@
|
|||||||
use actix_web::{web, HttpResponse, Responder};
|
use actix_web::{body::MessageBody, test::read_body, web, HttpResponse, Responder};
|
||||||
use askama_actix::TemplateToResponse;
|
use askama_actix::TemplateToResponse;
|
||||||
|
use brass_macros::db_test;
|
||||||
|
use insta::assert_snapshot;
|
||||||
|
|
||||||
use crate::{endpoints::area::NewOrEditAreaTemplate, models::{Role, User}};
|
use crate::{
|
||||||
|
endpoints::area::NewOrEditAreaTemplate,
|
||||||
|
models::{Role, User},
|
||||||
|
utils::test_helper::DbTestContext,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
use crate::utils::test_helper::{test_get, RequestConfig};
|
||||||
|
#[cfg(test)]
|
||||||
|
use actix_http::StatusCode;
|
||||||
|
|
||||||
#[actix_web::get("/area/new")]
|
#[actix_web::get("/area/new")]
|
||||||
async fn get(user: web::ReqData<User>) -> impl Responder {
|
async fn get(user: web::ReqData<User>) -> impl Responder {
|
||||||
@ -11,8 +22,25 @@ async fn get(user: web::ReqData<User>) -> impl Responder {
|
|||||||
|
|
||||||
let template = NewOrEditAreaTemplate {
|
let template = NewOrEditAreaTemplate {
|
||||||
user: user.into_inner(),
|
user: user.into_inner(),
|
||||||
area: None
|
area: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
template.to_response()
|
template.to_response()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[db_test]
|
||||||
|
async fn produces_template(context: &DbTestContext) {
|
||||||
|
let app = context.app().await;
|
||||||
|
let config = RequestConfig {
|
||||||
|
uri: "/area/new".to_string(),
|
||||||
|
role: Role::Admin,
|
||||||
|
function: crate::models::Function::Posten,
|
||||||
|
user_area: 1,
|
||||||
|
};
|
||||||
|
let response = test_get(&context.db_pool, app, &config).await;
|
||||||
|
|
||||||
|
assert_eq!(StatusCode::OK, response.status());
|
||||||
|
|
||||||
|
let body = String::from_utf8(read_body(response).await.to_vec()).unwrap();
|
||||||
|
assert_snapshot!(body);
|
||||||
|
}
|
||||||
|
@ -36,3 +36,9 @@ impl TryFrom<u8> for Function {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for Function {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::Posten
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,26 +1,11 @@
|
|||||||
use actix_http::StatusCode;
|
|
||||||
use actix_identity::Identity;
|
|
||||||
use actix_web::test;
|
|
||||||
use actix_web::test::call_service;
|
|
||||||
use actix_web::test::TestRequest;
|
|
||||||
use actix_web::HttpMessage;
|
|
||||||
use brass_macros::db_test;
|
|
||||||
use sqlx::{query, PgPool};
|
use sqlx::{query, PgPool};
|
||||||
|
|
||||||
use crate::auth::utils::generate_salt_and_hash_plain_password;
|
|
||||||
use crate::auth::utils::hash_plain_password_with_salt;
|
|
||||||
use crate::endpoints::user::post_login::LoginForm;
|
|
||||||
use crate::models::Function;
|
|
||||||
use crate::models::Role;
|
|
||||||
use crate::models::User;
|
|
||||||
use crate::utils::test_helper::DbTestContext;
|
|
||||||
|
|
||||||
use super::Area;
|
use super::Area;
|
||||||
|
|
||||||
use super::Result;
|
use super::Result;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Location {
|
pub struct Location {
|
||||||
pub id: i32,
|
pub id: i32,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub area_id: i32,
|
pub area_id: i32,
|
||||||
@ -133,55 +118,3 @@ impl Location {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[db_test]
|
|
||||||
async fn test_read_all(context: &DbTestContext) {
|
|
||||||
let app = context.app().await;
|
|
||||||
|
|
||||||
let (hash, salt) = generate_salt_and_hash_plain_password("abc").unwrap();
|
|
||||||
User::create_with_password(&context.db_pool, "abc", "abc", &hash, &salt, Role::Admin, Function::Wachhabender, 1).await.unwrap();
|
|
||||||
|
|
||||||
Location::create(&context.db_pool, "wundervolle location", 1).await.unwrap();
|
|
||||||
|
|
||||||
//let locations = Location::read_all(&context.db_pool).await.unwrap();
|
|
||||||
//
|
|
||||||
//assert_eq!(1, locations.len());
|
|
||||||
|
|
||||||
let login_form = LoginForm {
|
|
||||||
email: "abc".to_string(),
|
|
||||||
password: "abc".to_string()
|
|
||||||
};
|
|
||||||
|
|
||||||
let login_req = TestRequest::post()
|
|
||||||
.uri("/login")
|
|
||||||
.set_form(login_form)
|
|
||||||
.to_request();
|
|
||||||
|
|
||||||
let login_resp = call_service(&app, login_req).await;
|
|
||||||
//println!("{:?}", log);
|
|
||||||
let cookie = login_resp.response().cookies().next().unwrap().to_owned();
|
|
||||||
|
|
||||||
let req = TestRequest::get()
|
|
||||||
.uri("/locations")
|
|
||||||
.cookie(cookie)
|
|
||||||
.to_request();
|
|
||||||
|
|
||||||
//let http_req = TestRequest::get()
|
|
||||||
// .uri("/locations")
|
|
||||||
// .to_http_request();
|
|
||||||
//let service_req = TestRequest::get()
|
|
||||||
// .uri("/locations")
|
|
||||||
// .to_srv_request();
|
|
||||||
|
|
||||||
//Identity::login(&req.extensions(), "1".to_string()).unwrap();
|
|
||||||
let resp = call_service(&context.app().await, req).await;
|
|
||||||
//let resp = test::call_and_read_body(&context.app().await, http_req).await;
|
|
||||||
|
|
||||||
println!("{:?}", resp.headers());
|
|
||||||
assert_eq!(StatusCode::OK, resp.status());
|
|
||||||
|
|
||||||
|
|
||||||
let body = test::read_body(resp).await;
|
|
||||||
let body_string = String::from_utf8(body.to_vec()).unwrap();
|
|
||||||
assert!(body_string.contains("wundervolle location"));
|
|
||||||
}
|
|
||||||
|
@ -23,3 +23,9 @@ impl TryFrom<u8> for Role {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for Role {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::Staff
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -4,6 +4,8 @@ pub mod password_help;
|
|||||||
pub mod token_generation;
|
pub mod token_generation;
|
||||||
pub mod event_planning_template;
|
pub mod event_planning_template;
|
||||||
mod application_error;
|
mod application_error;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
pub mod test_helper;
|
pub mod test_helper;
|
||||||
|
|
||||||
pub use application_error::ApplicationError;
|
pub use application_error::ApplicationError;
|
||||||
|
@ -7,14 +7,20 @@ use actix_web::{
|
|||||||
test::init_service,
|
test::init_service,
|
||||||
};
|
};
|
||||||
use brass_config::{load_config, Config, Environment};
|
use brass_config::{load_config, Config, Environment};
|
||||||
use lettre::{transport::stub::StubTransport, Transport};
|
use lettre::transport::stub::StubTransport;
|
||||||
use rand::{distributions::Alphanumeric, thread_rng, Rng};
|
use rand::{distributions::Alphanumeric, thread_rng, Rng};
|
||||||
use regex::{Captures, Regex};
|
use regex::{Captures, Regex};
|
||||||
use sqlx::{
|
use sqlx::{
|
||||||
postgres::{PgConnectOptions, PgPoolOptions},
|
postgres::{PgConnectOptions, PgPoolOptions},
|
||||||
Connection, Executor, PgConnection, PgPool,
|
Connection, Executor, PgConnection, PgPool, Pool, Postgres,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
mod test_requests;
|
||||||
|
pub use test_requests::test_delete;
|
||||||
|
pub use test_requests::test_get;
|
||||||
|
pub use test_requests::test_post;
|
||||||
|
pub use test_requests::RequestConfig;
|
||||||
|
|
||||||
use crate::create_app;
|
use crate::create_app;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
131
web/src/utils/test_helper/test_requests.rs
Normal file
131
web/src/utils/test_helper/test_requests.rs
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
use crate::{
|
||||||
|
endpoints::user::post_login::LoginForm,
|
||||||
|
models::{Function, Role, User},
|
||||||
|
};
|
||||||
|
use actix_http::Request;
|
||||||
|
use actix_web::{
|
||||||
|
body::MessageBody,
|
||||||
|
cookie::Cookie,
|
||||||
|
dev::{Service, ServiceResponse},
|
||||||
|
error::Error,
|
||||||
|
test,
|
||||||
|
};
|
||||||
|
use serde::Serialize;
|
||||||
|
use sqlx::{Pool, Postgres};
|
||||||
|
|
||||||
|
pub struct RequestConfig {
|
||||||
|
pub uri: String,
|
||||||
|
pub role: Role,
|
||||||
|
pub function: Function,
|
||||||
|
pub user_area: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RequestConfig {
|
||||||
|
pub fn new(uri: &str) -> Self {
|
||||||
|
Self {
|
||||||
|
uri: uri.to_string(),
|
||||||
|
role: Role::Staff,
|
||||||
|
function: Function::Posten,
|
||||||
|
user_area: 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn create_user_and_get_login_cookie<'a, T, R>(
|
||||||
|
pool: &Pool<Postgres>,
|
||||||
|
app: &T,
|
||||||
|
config: &RequestConfig,
|
||||||
|
) -> Cookie<'a>
|
||||||
|
where
|
||||||
|
T: Service<Request, Response = ServiceResponse<R>, Error = Error>,
|
||||||
|
R: MessageBody + 'a,
|
||||||
|
{
|
||||||
|
const HASH: &str = "$argon2id$v=19$m=19456,t=2,p=1$IPiLaPCFZOK69MA1a6GUzw$ZZinpbkP7pXhN7g7dGkh87kGTeuFd/2er1U+y+4IKWo";
|
||||||
|
const SALT: &str = "IPiLaPCFZOK69MA1a6GUzw";
|
||||||
|
|
||||||
|
User::create_with_password(
|
||||||
|
pool,
|
||||||
|
"abc",
|
||||||
|
"abc",
|
||||||
|
&HASH,
|
||||||
|
&SALT,
|
||||||
|
config.role,
|
||||||
|
config.function,
|
||||||
|
config.user_area,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let login_form = LoginForm {
|
||||||
|
email: "abc".to_string(),
|
||||||
|
password: "abc".to_string(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let login_req = test::TestRequest::post()
|
||||||
|
.uri("/login")
|
||||||
|
.set_form(login_form)
|
||||||
|
.to_request();
|
||||||
|
|
||||||
|
let login_resp = test::call_service(&app, login_req).await;
|
||||||
|
login_resp.response().cookies().next().unwrap().into_owned()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn test_get<T, R>(
|
||||||
|
pool: &Pool<Postgres>,
|
||||||
|
app: T,
|
||||||
|
config: &RequestConfig,
|
||||||
|
) -> ServiceResponse<R>
|
||||||
|
where
|
||||||
|
T: Service<Request, Response = ServiceResponse<R>, Error = Error>,
|
||||||
|
R: MessageBody,
|
||||||
|
{
|
||||||
|
let cookie = create_user_and_get_login_cookie(&pool, &app, &config).await;
|
||||||
|
|
||||||
|
let get_request = test::TestRequest::get()
|
||||||
|
.uri(&config.uri)
|
||||||
|
.cookie(cookie)
|
||||||
|
.to_request();
|
||||||
|
|
||||||
|
test::call_service(&app, get_request).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn test_post<T, R, F>(
|
||||||
|
pool: &Pool<Postgres>,
|
||||||
|
app: T,
|
||||||
|
config: &RequestConfig,
|
||||||
|
form: F,
|
||||||
|
) -> ServiceResponse<R>
|
||||||
|
where
|
||||||
|
T: Service<Request, Response = ServiceResponse<R>, Error = Error>,
|
||||||
|
R: MessageBody,
|
||||||
|
F: Serialize,
|
||||||
|
{
|
||||||
|
let cookie = create_user_and_get_login_cookie(&pool, &app, config).await;
|
||||||
|
|
||||||
|
let post_request = test::TestRequest::post()
|
||||||
|
.uri(&config.uri)
|
||||||
|
.cookie(cookie)
|
||||||
|
.set_form(form)
|
||||||
|
.to_request();
|
||||||
|
|
||||||
|
test::call_service(&app, post_request).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn test_delete<T, R>(
|
||||||
|
pool: &Pool<Postgres>,
|
||||||
|
app: T,
|
||||||
|
config: &RequestConfig,
|
||||||
|
) -> ServiceResponse<R>
|
||||||
|
where
|
||||||
|
T: Service<Request, Response = ServiceResponse<R>, Error = Error>,
|
||||||
|
R: MessageBody,
|
||||||
|
{
|
||||||
|
let cookie = create_user_and_get_login_cookie(&pool, &app, config).await;
|
||||||
|
|
||||||
|
let delete_request = test::TestRequest::delete()
|
||||||
|
.uri(&config.uri)
|
||||||
|
.cookie(cookie)
|
||||||
|
.to_request();
|
||||||
|
|
||||||
|
test::call_service(&app, delete_request).await
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user