#![allow(dead_code)]
use actix_session::SessionMiddleware;
use actix_web::body::MessageBody;
use actix_web::cookie::{Key, SameSite};
use actix_web::dev::{ServiceFactory, ServiceRequest, ServiceResponse};
use actix_web::http::header;
use actix_web::{App, HttpRequest, HttpResponse, web};
use std::sync::{Arc, Mutex, Once, OnceLock};
use runegate::app::{AppSettings, build_renderer, configure_routes};
use runegate::config::{AppConfig, AuthUiMode, RunegateMode};
use runegate::email::EmailConfig;
use runegate::memory_session_store::MemorySessionStore;
use runegate::middleware::AuthMiddleware;
use runegate::rate_limit::RateLimiters;
use runegate::store::session::RunegateSessionStore;
pub const TEST_JWT_SECRET: &str = "integration-test-jwt-secret-0123456789abcdef";
pub const SESSION_COOKIE: &str = "runegate_id";
static INIT: Once = Once::new();
pub fn init_test_env() {
INIT.call_once(|| unsafe {
std::env::set_var("RUNEGATE_JWT_SECRET", TEST_JWT_SECRET);
std::env::set_var("RUNEGATE_RATE_LIMIT_ENABLED", "false");
std::env::set_var("RUNEGATE_DEBUG_ENDPOINTS", "true");
});
}
pub fn env_lock() -> std::sync::MutexGuard<'static, ()> {
static LOCK: OnceLock<Mutex<()>> = OnceLock::new();
LOCK.get_or_init(|| Mutex::new(()))
.lock()
.unwrap_or_else(|e| e.into_inner())
}
pub fn test_email_config() -> EmailConfig {
EmailConfig {
smtp_host: "127.0.0.1".to_string(),
smtp_port: 1,
smtp_user: "test@example.com".to_string(),
smtp_pass: "unused".to_string(),
from_address: "Runegate Test <test@example.com>".to_string(),
subject: "Your login link".to_string(),
body_template: "Login: {login_url} (valid {expiry_minutes} minutes)".to_string(),
}
}
pub fn legacy_app_config() -> AppConfig {
AppConfig {
base_url: "http://localhost:7870".to_string(),
email_config: test_email_config(),
google_oidc: None,
upload_private_key: None,
upload_jwks: None,
}
}
pub fn build_test_app(
mode: RunegateMode,
) -> App<
impl ServiceFactory<
ServiceRequest,
Config = (),
Response = ServiceResponse<impl MessageBody>,
Error = actix_web::Error,
InitError = (),
>,
> {
init_test_env();
let settings = AppSettings {
mode,
debug_endpoints_enabled: true,
};
let session_key = Key::from(&[7u8; 64]);
let session_store = RunegateSessionStore::Memory(MemorySessionStore::new());
let rate_limiters = web::Data::new(Arc::new(RateLimiters::new()));
let renderer = web::Data::from(build_renderer(AuthUiMode::Static));
let app_config = web::Data::new(legacy_app_config());
App::new()
.wrap(AuthMiddleware::with_mode(mode))
.wrap(
SessionMiddleware::builder(session_store, session_key)
.cookie_secure(false)
.cookie_http_only(true)
.cookie_same_site(SameSite::Lax)
.cookie_path("/".to_string())
.cookie_name(SESSION_COOKIE.to_string())
.build(),
)
.app_data(app_config)
.app_data(rate_limiters)
.app_data(renderer)
.app_data(web::Data::new(settings))
.configure(move |cfg| configure_routes(cfg, &settings))
}
pub fn extract_session_cookie<B>(resp: &ServiceResponse<B>) -> Option<String> {
resp.headers()
.get_all(header::SET_COOKIE)
.filter_map(|v| v.to_str().ok())
.find(|v| v.starts_with(&format!("{}=", SESSION_COOKIE)))
.map(|v| v.split(';').next().unwrap_or("").to_string())
}
pub fn raw_session_set_cookie<B>(resp: &ServiceResponse<B>) -> Option<String> {
resp.headers()
.get_all(header::SET_COOKIE)
.filter_map(|v| v.to_str().ok())
.find(|v| v.starts_with(&format!("{}=", SESSION_COOKIE)))
.map(|v| v.to_string())
}
async fn echo_upstream(req: HttpRequest, body: web::Bytes) -> HttpResponse {
let headers: std::collections::HashMap<String, String> = req
.headers()
.iter()
.map(|(k, v)| {
(
k.as_str().to_lowercase(),
v.to_str().unwrap_or("").to_string(),
)
})
.collect();
HttpResponse::Ok().json(serde_json::json!({
"upstream": true,
"path": req.path(),
"method": req.method().as_str(),
"body_len": body.len(),
"headers": headers,
}))
}
pub fn spawn_upstream() -> actix_test::TestServer {
actix_test::start(|| App::new().default_service(web::route().to(echo_upstream)))
}
pub fn point_proxy_at(srv: &actix_test::TestServer) {
let url = srv.url("");
let url = url.trim_end_matches('/');
unsafe { std::env::set_var("RUNEGATE_TARGET_SERVICE", url) };
}