use async_trait::async_trait;
use better_auth::adapters::MemoryDatabaseAdapter;
use better_auth::plugins::{EmailPasswordPlugin, SessionManagementPlugin};
use better_auth::types::{AuthRequest, AuthResponse, HttpMethod};
use better_auth::{AuthBuilder, AuthConfig, AuthContext, AuthPlugin, AuthResult, AuthRoute};
use serde_json::json;
struct RouteTestPlugin;
#[async_trait]
impl AuthPlugin<MemoryDatabaseAdapter> for RouteTestPlugin {
fn name(&self) -> &'static str {
"route-test"
}
fn routes(&self) -> Vec<AuthRoute> {
vec![
AuthRoute::get("/route-test", "route_test"),
AuthRoute::post("/route-test", "route_test_post"),
]
}
async fn on_request(
&self,
_req: &AuthRequest,
_ctx: &AuthContext<MemoryDatabaseAdapter>,
) -> AuthResult<Option<AuthResponse>> {
Ok(None)
}
}
fn test_config() -> AuthConfig {
AuthConfig::new("test-secret-key-that-is-at-least-32-characters-long")
.base_url("http://localhost:3000")
.password_min_length(6)
}
#[tokio::test]
async fn test_routes_include_plugin_routes() {
let auth = AuthBuilder::new(test_config())
.database(MemoryDatabaseAdapter::new())
.plugin(RouteTestPlugin)
.build()
.await
.expect("Failed to build auth instance");
let routes = auth.routes();
let has_plugin_route = routes.iter().any(|(path, _)| path == "/route-test");
assert!(has_plugin_route, "Expected plugin route /route-test");
let plugin_names = auth.plugin_names();
assert!(
plugin_names.contains(&"route-test"),
"Expected route-test plugin to be registered"
);
}
#[tokio::test]
async fn test_signup_and_delete_lifecycle() {
let auth = AuthBuilder::new(test_config())
.database(MemoryDatabaseAdapter::new())
.plugin(EmailPasswordPlugin::new().enable_signup(true))
.plugin(SessionManagementPlugin::new())
.build()
.await
.expect("Failed to build auth instance");
let signup_data = json!({
"email": "lifecycle@example.com",
"password": "password123",
"name": "Lifecycle User"
});
let mut signup_request = AuthRequest::new(HttpMethod::Post, "/sign-up/email");
signup_request.body = Some(signup_data.to_string().into_bytes());
signup_request
.headers
.insert("content-type".to_string(), "application/json".to_string());
let signup_response = auth
.handle_request(signup_request)
.await
.expect("Signup failed");
assert_eq!(signup_response.status, 200);
let response_json: serde_json::Value =
serde_json::from_slice(&signup_response.body).expect("Failed to parse response JSON");
assert!(response_json["user"]["id"].is_string());
assert_eq!(response_json["user"]["email"], "lifecycle@example.com");
let token = response_json["token"].as_str().unwrap().to_string();
let mut delete_request = AuthRequest::new(HttpMethod::Delete, "/delete-user");
delete_request
.headers
.insert("authorization".to_string(), format!("Bearer {}", token));
let delete_response = auth
.handle_request(delete_request)
.await
.expect("Delete failed");
assert_eq!(delete_response.status, 200);
let mut retry_request = AuthRequest::new(HttpMethod::Delete, "/delete-user");
retry_request
.headers
.insert("authorization".to_string(), format!("Bearer {}", token));
let retry_response = auth
.handle_request(retry_request)
.await
.expect("Request should not panic");
assert_eq!(retry_response.status, 401);
}