131 lines
4.9 KiB
Rust
131 lines
4.9 KiB
Rust
// cfg(test) only — this whole module is test-only
|
|
use sqlx::SqlitePool;
|
|
use axum::Router;
|
|
use tower::ServiceExt;
|
|
use axum::http::{Request, StatusCode};
|
|
use http_body_util::BodyExt;
|
|
|
|
/// Insert a test tutor (if not exists), return a valid JWT for that tutor.
|
|
pub async fn make_token(pool: &SqlitePool, email: &str, is_superadmin: bool) -> String {
|
|
let hash = bcrypt::hash("testpass", 4).unwrap();
|
|
sqlx::query("INSERT OR IGNORE INTO tutors (name,email,password_hash,is_superadmin) VALUES (?,?,?,?)")
|
|
.bind("Test Tutor").bind(email).bind(&hash).bind(is_superadmin)
|
|
.execute(pool).await.unwrap();
|
|
|
|
// Ensure the superadmin flag is correct even if it existed
|
|
sqlx::query("UPDATE tutors SET is_superadmin = ? WHERE email = ?")
|
|
.bind(is_superadmin).bind(email).execute(pool).await.unwrap();
|
|
|
|
let row: (i64, bool) = sqlx::query_as("SELECT id, is_superadmin FROM tutors WHERE email = ?")
|
|
.bind(email).fetch_one(pool).await.unwrap();
|
|
unsafe { std::env::set_var("JWT_SECRET", "testsecret"); }
|
|
crate::auth::encode_jwt(row.0, email, row.1).unwrap()
|
|
}
|
|
|
|
/// Build the full Axum app wired with the given pool, plus a Bearer auth header value.
|
|
pub async fn build_test_app(pool: SqlitePool) -> (Router, String) {
|
|
unsafe { std::env::set_var("JWT_SECRET", "testsecret"); }
|
|
let token = make_token(&pool, "tutor@test.com", false).await;
|
|
let app = crate::routes::build(pool);
|
|
(app, format!("Bearer {token}"))
|
|
}
|
|
|
|
/// Build the full Axum app wired with a superadmin Bearer auth header.
|
|
pub async fn build_test_admin_app(pool: SqlitePool) -> (Router, String) {
|
|
unsafe { std::env::set_var("JWT_SECRET", "testsecret"); }
|
|
let token = make_token(&pool, "admin@test.com", true).await;
|
|
let app = crate::routes::build(pool);
|
|
(app, format!("Bearer {token}"))
|
|
}
|
|
|
|
/// POST JSON body to the app (one-shot), returns (StatusCode, response body bytes).
|
|
pub async fn post_json(app: Router, path: &str, auth: &str, body: serde_json::Value)
|
|
-> (StatusCode, bytes::Bytes)
|
|
{
|
|
let mut builder = Request::builder()
|
|
.method("POST").uri(path)
|
|
.header("Content-Type", "application/json");
|
|
if !auth.is_empty() {
|
|
builder = builder.header("Authorization", auth);
|
|
}
|
|
let req = builder
|
|
.body(axum::body::Body::from(body.to_string()))
|
|
.unwrap();
|
|
let res = app.oneshot(req).await.unwrap();
|
|
let status = res.status();
|
|
let body = res.into_body().collect().await.unwrap().to_bytes();
|
|
(status, body)
|
|
}
|
|
|
|
/// PUT JSON body to the app (one-shot), returns (StatusCode, response body bytes).
|
|
pub async fn put_json(app: Router, uri: &str, auth: &str, body: serde_json::Value)
|
|
-> (StatusCode, bytes::Bytes)
|
|
{
|
|
let mut req = Request::builder()
|
|
.method("PUT")
|
|
.uri(uri)
|
|
.header("Content-Type", "application/json");
|
|
if !auth.is_empty() {
|
|
req = req.header("Authorization", auth);
|
|
}
|
|
let req = req
|
|
.body(axum::body::Body::from(body.to_string()))
|
|
.unwrap();
|
|
let res = app.oneshot(req).await.unwrap();
|
|
let status = res.status();
|
|
let body = res.into_body().collect().await.unwrap().to_bytes();
|
|
(status, body)
|
|
}
|
|
|
|
/// PATCH JSON body to the app (one-shot), returns (StatusCode, response body bytes).
|
|
pub async fn patch_json(
|
|
app: Router,
|
|
uri: &str,
|
|
auth: &str,
|
|
body: serde_json::Value,
|
|
) -> (StatusCode, bytes::Bytes) {
|
|
let mut req = Request::builder()
|
|
.method("PATCH")
|
|
.uri(uri)
|
|
.header("Content-Type", "application/json");
|
|
if !auth.is_empty() {
|
|
req = req.header("Authorization", auth);
|
|
}
|
|
let req = req
|
|
.body(axum::body::Body::from(body.to_string()))
|
|
.unwrap();
|
|
let res = app.oneshot(req).await.unwrap();
|
|
let status = res.status();
|
|
let body = res.into_body().collect().await.unwrap().to_bytes();
|
|
(status, body)
|
|
}
|
|
|
|
/// GET from the app (one-shot), returns (StatusCode, response body bytes).
|
|
pub async fn get(app: Router, path: &str, auth: &str) -> (StatusCode, bytes::Bytes) {
|
|
let mut builder = Request::builder()
|
|
.method("GET").uri(path);
|
|
if !auth.is_empty() {
|
|
builder = builder.header("Authorization", auth);
|
|
}
|
|
let req = builder
|
|
.body(axum::body::Body::empty())
|
|
.unwrap();
|
|
let res = app.oneshot(req).await.unwrap();
|
|
let status = res.status();
|
|
let body = res.into_body().collect().await.unwrap().to_bytes();
|
|
(status, body)
|
|
}
|
|
|
|
/// DELETE from the app (one-shot), returns (StatusCode, response body bytes).
|
|
pub async fn delete(app: Router, path: &str, auth: &str) -> (StatusCode, bytes::Bytes) {
|
|
let mut builder = Request::builder().method("DELETE").uri(path);
|
|
if !auth.is_empty() {
|
|
builder = builder.header("Authorization", auth);
|
|
}
|
|
let req = builder.body(axum::body::Body::empty()).unwrap();
|
|
let res = app.oneshot(req).await.unwrap();
|
|
let status = res.status();
|
|
let body = res.into_body().collect().await.unwrap().to_bytes();
|
|
(status, body)
|
|
}
|