feat: fully working integration test

This commit is contained in:
Max Hohlfeld 2024-12-11 23:52:56 +01:00
parent bf4e7b764e
commit ed7ce14990
9 changed files with 192 additions and 71 deletions

66
Cargo.lock generated
View File

@ -31,7 +31,7 @@ dependencies = [
"actix-web",
"bitflags 2.6.0",
"bytes",
"derive_more",
"derive_more 0.99.18",
"futures-core",
"http-range",
"log",
@ -58,7 +58,7 @@ dependencies = [
"brotli",
"bytes",
"bytestring",
"derive_more",
"derive_more 0.99.18",
"encoding_rs",
"flate2",
"futures-core",
@ -83,15 +83,15 @@ dependencies = [
[[package]]
name = "actix-identity"
version = "0.5.2"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1224c9f9593dc27c9077b233ce04adedc1d7febcfc35ee9f53ea3c24df180bec"
checksum = "23b8ddc6f6a8b19c4016aaa13519968da9969bc3bc1c1c883cdb0f25dd6c8cf7"
dependencies = [
"actix-service",
"actix-session",
"actix-utils",
"actix-web",
"anyhow",
"derive_more 1.0.0",
"futures-core",
"serde",
"tracing",
@ -162,16 +162,16 @@ dependencies = [
[[package]]
name = "actix-session"
version = "0.7.2"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43da8b818ae1f11049a4d218975345fe8e56ce5a5f92c11f972abcff5ff80e87"
checksum = "efe6976a74f34f1b6d07a6c05aadc0ed0359304a7781c367fa5b4029418db08f"
dependencies = [
"actix-service",
"actix-utils",
"actix-web",
"anyhow",
"async-trait",
"derive_more",
"derive_more 1.0.0",
"rand",
"serde",
"serde_json",
"tracing",
@ -207,7 +207,7 @@ dependencies = [
"bytestring",
"cfg-if",
"cookie",
"derive_more",
"derive_more 0.99.18",
"encoding_rs",
"futures-core",
"futures-util",
@ -248,7 +248,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adf6d1ef6d7a60e084f9e0595e2a5234abda14e76c105ecf8e2d0e8800c41a1f"
dependencies = [
"actix-web",
"derive_more",
"derive_more 0.99.18",
"futures-util",
"static-files",
]
@ -731,6 +731,7 @@ name = "brass-web"
version = "0.1.0"
dependencies = [
"actix-files",
"actix-http",
"actix-identity",
"actix-session",
"actix-web",
@ -1098,6 +1099,27 @@ dependencies = [
"syn",
]
[[package]]
name = "derive_more"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05"
dependencies = [
"derive_more-impl",
]
[[package]]
name = "derive_more-impl"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22"
dependencies = [
"proc-macro2",
"quote",
"syn",
"unicode-xid",
]
[[package]]
name = "digest"
version = "0.10.7"
@ -2452,9 +2474,9 @@ dependencies = [
[[package]]
name = "redox_syscall"
version = "0.5.7"
version = "0.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f"
checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834"
dependencies = [
"bitflags 2.6.0",
]
@ -2573,9 +2595,9 @@ dependencies = [
[[package]]
name = "rustls"
version = "0.23.19"
version = "0.23.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "934b404430bb06b3fae2cba809eb45a1ab1aecd64491213d7c3301b88393f8d1"
checksum = "5065c3f250cbd332cd894be57c40fa52387247659b14a2d6041d121547903b1b"
dependencies = [
"once_cell",
"ring",
@ -2663,18 +2685,18 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
[[package]]
name = "serde"
version = "1.0.215"
version = "1.0.216"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f"
checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.215"
version = "1.0.216"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0"
checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e"
dependencies = [
"proc-macro2",
"quote",
@ -3285,6 +3307,12 @@ version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0"
[[package]]
name = "unicode-xid"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
[[package]]
name = "unicode_categories"
version = "0.1.1"

View File

@ -2,6 +2,7 @@ use anyhow::anyhow;
use std::env;
use std::net::IpAddr;
#[derive(Clone, Debug)]
pub struct Config {
/// the ip the server will bind to, e.g. 127.0.0.1 or ::1
pub server_address: IpAddr,
@ -18,6 +19,7 @@ pub struct Config {
pub smtp_tlstype: SmtpTlsType,
}
#[derive(Clone, Debug)]
pub enum SmtpTlsType {
TLS,
StartTLS,

View File

@ -15,8 +15,8 @@ serde = { version = "1.0.164", features = ["derive"] }
argon2 = { version = "0.5.0", features = [ "std"]}
anyhow = "1.0.71"
dotenv = "0.15.0"
actix-session = { version = "0.7.2", features = ["cookie-session"] }
actix-identity = "0.5.2"
actix-session = { version = "0.10.1", features = ["cookie-session"] }
actix-identity = "0.8.0"
chrono = { version = "0.4.33", features = ["serde", "now"] }
actix-files = "0.6.5"
askama_actix = "0.14.0"
@ -35,6 +35,7 @@ idna = "=1.0.2"
regex = "1.11.1"
brass-macros = { path = "../macros" }
brass-config = { path = "../config" }
actix-http = "3.9.0"
[build-dependencies]
built = "0.7.4"

View File

@ -8,7 +8,7 @@ mod availability;
mod events;
mod export;
mod location;
mod user;
pub mod user;
mod imprint;
mod vehicle;

View File

@ -1,14 +1,14 @@
use actix_identity::Identity;
use actix_web::{web, HttpMessage, HttpRequest, HttpResponse, Responder};
use serde::Deserialize;
use serde::{Deserialize, Serialize};
use sqlx::PgPool;
use crate::{auth::utils::hash_plain_password_with_salt, models::User};
#[derive(Deserialize)]
struct LoginForm {
email: String,
password: String,
#[derive(Deserialize, Serialize)]
pub struct LoginForm {
pub email: String,
pub password: String,
}
#[actix_web::post("/login")]

View File

@ -3,11 +3,15 @@ use std::time::Duration;
use actix_identity::IdentityMiddleware;
use actix_session::SessionMiddleware;
use actix_web::body::MessageBody;
use actix_web::cookie::Key;
use actix_web::dev::{ServiceFactory, ServiceRequest, ServiceResponse};
use actix_web::{web, App, HttpServer};
use actix_web_static_files::ResourceFiles;
use brass_config::{get_env, load_config, Config};
use lettre::Transport;
use sqlx::postgres::PgPool;
use sqlx::{Pool, Postgres};
use crate::postgres_session_store::SqlxPostgresqlSessionStore;
use crate::utils::manage_commands::{handle_command, parse_args};
@ -33,19 +37,41 @@ async fn main() -> anyhow::Result<()> {
let pool = PgPool::connect(&config.database_url).await?;
let mailer = utils::email::get_mailer()?;
let secret_key = Key::from(config.secret_key.as_bytes());
let store = SqlxPostgresqlSessionStore::from_pool(pool.clone().into());
handle_command(args.command, &pool).await?;
println!("Starting server on http://{}:{}.", config.server_address, config.server_port);
let address = config.server_address;
let port = config.server_port;
println!("Starting server on http://{address}:{port}.",);
HttpServer::new(move || {
HttpServer::new(move || create_app(config.clone(), pool.clone(), mailer.clone()))
.bind((address, port))?
.run()
.await?;
Ok(())
}
pub fn create_app<T>(
config: Config,
pool: Pool<Postgres>,
mailer: T,
) -> App<
impl ServiceFactory<
ServiceRequest,
Response = ServiceResponse<impl MessageBody>,
Config = (),
InitError = (),
Error = actix_web::error::Error,
>,
> where T: Transport + 'static {
let generated = generate();
let secret_key = Key::from(config.secret_key.as_bytes());
let store = SqlxPostgresqlSessionStore::from_pool(pool.clone().into());
App::new()
.app_data(web::Data::new(pool.clone()))
.app_data(web::Data::new(mailer.clone()))
.app_data(web::Data::new(pool))
.app_data(web::Data::new(mailer))
.configure(endpoints::init)
.wrap(middleware::RedirectToLogin)
.wrap(middleware::LoadCurrentUser)
@ -56,10 +82,4 @@ async fn main() -> anyhow::Result<()> {
)
.wrap(SessionMiddleware::new(store.clone(), secret_key.clone()))
.service(ResourceFiles::new("/static", generated))
})
.bind((config.server_address, config.server_port))?
.run()
.await?;
Ok(())
}

View File

@ -1,14 +1,24 @@
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 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::Result;
//use macros::db_test;
#[derive(Debug)]
pub struct Location {
pub id: i32,
@ -126,10 +136,52 @@ impl Location {
#[db_test]
async fn test_read_all(context: &DbTestContext) {
println!("im test {:#?}", context.db_pool.connect_options());
Location::create(&context.db_pool, "abc", 1).await.unwrap();
let app = context.app().await;
let locations = Location::read_all(&context.db_pool).await.unwrap();
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();
assert_eq!(1, locations.len());
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"));
}

View File

@ -49,7 +49,6 @@ impl SqlxPostgresqlSessionStore {
pub(crate) type SessionState = HashMap<String, String>;
#[async_trait::async_trait(?Send)]
impl SessionStore for SqlxPostgresqlSessionStore {
async fn load(&self, session_key: &SessionKey) -> Result<Option<SessionState>, LoadError> {
let key = (self.configuration.cache_keygen)(session_key.as_ref());

View File

@ -1,17 +1,44 @@
use std::{cell::OnceCell, str::FromStr, sync::Arc};
use actix_http::Request;
use actix_web::{
body::MessageBody,
dev::{Service, ServiceResponse},
test::init_service,
};
use brass_config::{load_config, Config, Environment};
use lettre::{transport::stub::StubTransport, Transport};
use rand::{distributions::Alphanumeric, thread_rng, Rng};
use regex::{Captures, Regex};
use sqlx::{postgres::{PgConnectOptions, PgPoolOptions}, Connection, Executor, PgConnection, PgPool};
use sqlx::{
postgres::{PgConnectOptions, PgPoolOptions},
Connection, Executor, PgConnection, PgPool,
};
use crate::create_app;
#[derive(Debug)]
pub struct DbTestContext {
//pub app: Router,
pub db_pool: PgPool,
config: Config,
}
impl DbTestContext {
pub async fn app(
&self,
) -> impl Service<
Request,
Response = ServiceResponse<impl MessageBody>,
Error = actix_web::error::Error,
> {
init_service(create_app(
self.config.clone(),
self.db_pool.clone(),
StubTransport::new_ok(),
))
.await
}
}
#[allow(unused)]
pub async fn setup() -> DbTestContext {
@ -20,17 +47,12 @@ pub async fn setup() -> DbTestContext {
let test_db_pool = setup_db(&config.database_url).await;
//let app = init_routes(AppState {
// db_pool: test_db_pool.clone(),
//});
let abc = DbTestContext {
//app,
let context = DbTestContext {
config: config.clone(),
db_pool: test_db_pool,
};
println!("{abc:#?}");
abc
context
}
#[allow(unused)]
@ -45,10 +67,7 @@ pub async fn setup_db(url: &str) -> PgPool {
let test_db_config = prepare_db(url).await;
println!("{test_db_config}");
let pool = PgPoolOptions::new()
.connect(&test_db_config)
.await
.unwrap();
let pool = PgPoolOptions::new().connect(&test_db_config).await.unwrap();
pool
}