mod compat;
use compat::helpers::*;
use proptest::prelude::*;
proptest! {
#[test]
fn short_passwords_are_rejected(password in "[a-zA-Z0-9]{1,7}") {
let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(async {
let auth = create_test_auth().await;
let (status, body) = send_request(
&auth,
post_json(
"/sign-up/email",
serde_json::json!({
"name": "Prop User",
"email": "proptest_short@example.com",
"password": password
}),
),
)
.await;
prop_assert_eq!(
status, 400,
"Password '{}' (len={}) should be rejected, got status {} body: {}",
password, password.len(), status, body
);
Ok(())
})?;
}
}
proptest! {
#![proptest_config(ProptestConfig::with_cases(20))]
#[test]
fn valid_length_passwords_not_rejected_for_length(
password in "[a-zA-Z0-9!@#$%]{8,64}"
) {
let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(async {
let auth = create_test_auth().await;
let email = unique_email("proplen");
let (status, body) = send_request(
&auth,
post_json(
"/sign-up/email",
serde_json::json!({
"name": "Prop Len User",
"email": email,
"password": password
}),
),
)
.await;
if status == 400 {
let msg = body["message"].as_str().unwrap_or("");
prop_assert!(
!msg.contains("Password must be at least"),
"Password '{}' (len={}) wrongly rejected for length: {}",
password, password.len(), msg
);
}
Ok(())
})?;
}
}
proptest! {
#![proptest_config(ProptestConfig::with_cases(20))]
#[test]
fn invalid_emails_are_rejected(
local in "[a-z]{1,10}",
) {
let email = local.clone(); let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(async {
let auth = create_test_auth().await;
let (status, _body) = send_request(
&auth,
post_json(
"/sign-up/email",
serde_json::json!({
"name": "Invalid Email",
"email": email,
"password": "password123"
}),
),
)
.await;
prop_assert!(
status >= 400,
"Email '{}' should be rejected, got status {}",
email, status
);
Ok(())
})?;
}
}
proptest! {
#[test]
fn valid_token_format_accepted(suffix in "[a-zA-Z0-9]{34,80}") {
let token = format!("session_{}", suffix);
let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(async {
let auth = create_test_auth().await;
let session_mgr = auth.session_manager();
prop_assert!(
session_mgr.validate_token_format(&token),
"Token '{}' (len={}) should be valid",
token, token.len()
);
Ok(())
})?;
}
}
proptest! {
#[test]
fn invalid_token_format_rejected(token in "[a-zA-Z0-9_]{0,39}") {
let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(async {
let auth = create_test_auth().await;
let session_mgr = auth.session_manager();
if !token.starts_with("session_") || token.len() <= 40 {
prop_assert!(
!session_mgr.validate_token_format(&token),
"Token '{}' (len={}) should be invalid",
token, token.len()
);
}
Ok(())
})?;
}
}
proptest! {
#[test]
fn short_secrets_fail_validation(secret in "[a-zA-Z0-9]{1,31}") {
let config = better_auth::AuthConfig::new(&secret);
prop_assert!(
config.validate().is_err(),
"Secret of length {} should fail validation",
secret.len()
);
}
}
proptest! {
#![proptest_config(ProptestConfig::with_cases(20))]
#[test]
fn long_secrets_pass_validation(secret in "[a-zA-Z0-9]{32,128}") {
let config = better_auth::AuthConfig::new(&secret);
prop_assert!(
config.validate().is_ok(),
"Secret of length {} should pass validation",
secret.len()
);
}
}