Files
kakeibo/backend/tests/common/mod.rs
T

251 lines
8.6 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! Entegrasyon testleri için yardımcılar.
//!
//! Her test kendi geçici veritabanını (`kakeibo_test_<uuid>`) kullanır ve sonunda siler.
//! Gereken: yerel MongoDB çalışıyor olmalı (`npm run db:up`), `.env` içinde `MONGODB_URI`.
#![allow(dead_code)] // her test dosyası tüm yardımcıları kullanmaz
use axum::{
Router,
body::Body,
http::{Method, Request, StatusCode, header},
};
use http_body_util::BodyExt;
use kakeibo::{
config::{Config, RegistrationMode},
db, routes,
state::AppState,
};
use mongodb::Database;
use serde_json::{Value, json};
use tower::ServiceExt;
pub struct TestApp {
router: Router,
pub db: Database,
}
/// Oturum açmış bir test kullanıcısı
#[derive(Clone)]
pub struct Session {
pub cookie: String,
pub household_id: String,
}
impl TestApp {
/// Kayıt açık (birden fazla hane gerektiren testler için).
pub async fn new() -> Self {
Self::with_registration(RegistrationMode::Open).await
}
pub async fn with_registration(registration: RegistrationMode) -> Self {
dotenvy::dotenv().ok();
let uri = std::env::var("MONGODB_URI")
.expect("MONGODB_URI tanımlı değil (.env dosyasını kontrol edin)");
let base = db::connect(&uri)
.await
.expect("MongoDB'ye bağlanılamadı — `npm run db:up` çalıştırın");
let name = format!("kakeibo_test_{}", uuid::Uuid::new_v4().simple());
let db = base.client().database(&name);
db::prepare(&db).await.expect("test DB hazırlanamadı");
let config = Config {
mongodb_uri: uri,
jwt_secret: "test-jwt-secret-en-az-32-karakter-olmali!!".into(),
app_url: "http://localhost".into(),
port: 0,
registration,
static_dir: None,
rates_refresh_minutes: 0,
turnstile_site_key: None,
turnstile_secret_key: None,
trust_proxy: true,
};
let router = routes::router(AppState::new(db.clone(), config));
Self { router, db }
}
/// Bot doğrulaması açık (ağsız sabit sonuç) bir uygulama: giriş/kayıt güvenlik testleri
pub async fn with_captcha(pass: bool) -> Self {
let app = Self::new().await;
dotenvy::dotenv().ok();
let config = Config {
mongodb_uri: std::env::var("MONGODB_URI").unwrap(),
jwt_secret: "test-jwt-secret-en-az-32-karakter-olmali!!".into(),
app_url: "http://localhost".into(),
port: 0,
registration: RegistrationMode::Open,
static_dir: None,
rates_refresh_minutes: 0,
turnstile_site_key: Some("test-site-key".into()),
turnstile_secret_key: Some("test-secret".into()),
trust_proxy: true,
};
let state = AppState::new(app.db.clone(), config)
.with_captcha(kakeibo::auth::captcha::Captcha::Fixed(pass));
Self {
router: routes::router(state),
db: app.db,
}
}
pub fn router_clone(&self) -> Router {
self.router.clone()
}
/// Ham istek (başlık eklemek için): (durum, gövde, çerez)
pub async fn send_with(
&self,
req: axum::http::Request<Body>,
) -> (StatusCode, Value, Option<String>) {
let res = self.router.clone().oneshot(req).await.unwrap();
let status = res.status();
let set_cookie = res
.headers()
.get(header::SET_COOKIE)
.and_then(|v| v.to_str().ok())
.and_then(|v| v.split(';').next())
.map(String::from);
let bytes = res.into_body().collect().await.unwrap().to_bytes();
(
status,
serde_json::from_slice(&bytes).unwrap_or(Value::Null),
set_cookie,
)
}
/// Test sonunda geçici veritabanını siler.
pub async fn cleanup(self) {
self.db.drop().await.ok();
}
/// İstek gönderir; (durum kodu, JSON gövde, set-cookie) döner.
pub async fn send(
&self,
method: Method,
path: &str,
cookie: Option<&str>,
body: Option<Value>,
) -> (StatusCode, Value, Option<String>) {
let mut req = Request::builder().method(method).uri(format!("/api{path}"));
if let Some(c) = cookie {
req = req.header(header::COOKIE, c);
}
let req = match body {
Some(b) => req
.header(header::CONTENT_TYPE, "application/json")
.body(Body::from(b.to_string())),
None => req.body(Body::empty()),
}
.unwrap();
let res = self.router.clone().oneshot(req).await.unwrap();
let status = res.status();
let set_cookie = res
.headers()
.get(header::SET_COOKIE)
.and_then(|v| v.to_str().ok())
.and_then(|v| v.split(';').next())
.map(String::from);
let bytes = res.into_body().collect().await.unwrap().to_bytes();
let json = serde_json::from_slice(&bytes).unwrap_or(Value::Null);
(status, json, set_cookie)
}
/// Düz metin gövde gönderir (CSV içe aktarma)
pub async fn post_text(&self, path: &str, s: &Session, text: &str) -> (StatusCode, Value) {
let req = Request::builder()
.method(Method::POST)
.uri(format!("/api{path}"))
.header(header::COOKIE, &s.cookie)
.header(header::CONTENT_TYPE, "text/csv; charset=utf-8")
.body(Body::from(text.to_string()))
.unwrap();
let res = self.router.clone().oneshot(req).await.unwrap();
let status = res.status();
let bytes = res.into_body().collect().await.unwrap().to_bytes();
(
status,
serde_json::from_slice(&bytes).unwrap_or(Value::Null),
)
}
/// Metin yanıt (CSV dışa aktarma): (durum, içerik türü, gövde)
pub async fn get_text(&self, path: &str, s: &Session) -> (StatusCode, String, String) {
let req = Request::builder()
.uri(format!("/api{path}"))
.header(header::COOKIE, &s.cookie)
.body(Body::empty())
.unwrap();
let res = self.router.clone().oneshot(req).await.unwrap();
let status = res.status();
let ct = res
.headers()
.get(header::CONTENT_TYPE)
.and_then(|v| v.to_str().ok())
.unwrap_or_default()
.to_string();
let bytes = res.into_body().collect().await.unwrap().to_bytes();
(status, ct, String::from_utf8(bytes.to_vec()).unwrap())
}
pub async fn get(&self, path: &str, s: &Session) -> (StatusCode, Value) {
let (st, v, _) = self.send(Method::GET, path, Some(&s.cookie), None).await;
(st, v)
}
pub async fn post(&self, path: &str, s: &Session, body: Value) -> (StatusCode, Value) {
let (st, v, _) = self
.send(Method::POST, path, Some(&s.cookie), Some(body))
.await;
(st, v)
}
pub async fn patch(&self, path: &str, s: &Session, body: Value) -> (StatusCode, Value) {
let (st, v, _) = self
.send(Method::PATCH, path, Some(&s.cookie), Some(body))
.await;
(st, v)
}
pub async fn put(&self, path: &str, s: &Session, body: Value) -> (StatusCode, Value) {
let (st, v, _) = self
.send(Method::PUT, path, Some(&s.cookie), Some(body))
.await;
(st, v)
}
pub async fn delete(&self, path: &str, s: &Session) -> (StatusCode, Value) {
let (st, v, _) = self.send(Method::DELETE, path, Some(&s.cookie), None).await;
(st, v)
}
/// Yeni kullanıcı kaydeder (davet koduyla veya yeni hane olarak) ve oturumunu döner.
pub async fn register(&self, name: &str, invite_code: Option<&str>) -> Session {
let email = format!(
"{}-{}@test.com",
name.to_lowercase(),
uuid::Uuid::new_v4().simple()
);
let (status, body, cookie) = self
.send(
Method::POST,
"/auth/register",
None,
Some(json!({
"name": name,
"email": email,
"password": "test-sifre-123",
"invite_code": invite_code,
})),
)
.await;
assert_eq!(status, StatusCode::CREATED, "kayıt başarısız: {body}");
Session {
cookie: cookie.expect("kayıtta cookie dönmedi"),
household_id: body["household"]["id"].as_str().unwrap().to_string(),
}
}
}