test: add unit tests for auth middleware cookie extraction

Extract cookie parsing into a standalone `extract_session_token` function
and add 5 unit tests covering the valid, missing, multi-cookie, whitespace,
and empty-header cases.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
master
oabrivard 3 months ago
parent 7b3fc717eb
commit a4e618feda

@ -28,6 +28,20 @@ pub struct AuthUser {
pub role: UserRole,
}
/// Extract session token from a Cookie header value.
///
/// Splits the header on `;`, trims whitespace, and returns the value of the
/// first cookie whose name matches `cookie_name`. Returns `None` when no
/// matching cookie is present.
fn extract_session_token(cookie_header: &str, cookie_name: &str) -> Option<String> {
cookie_header
.split(';')
.map(|s| s.trim())
.find(|s| s.starts_with(&format!("{}=", cookie_name)))
.and_then(|s| s.strip_prefix(&format!("{}=", cookie_name)))
.map(|s| s.to_string())
}
impl FromRequestParts<AppState> for AuthUser {
type Rejection = AppError;
@ -36,7 +50,6 @@ impl FromRequestParts<AppState> for AuthUser {
state: &AppState,
) -> impl std::future::Future<Output = Result<Self, Self::Rejection>> + Send {
let pool = state.pool.clone();
let cookie_prefix = format!("{}=", SESSION_COOKIE_NAME);
// Extract the session cookie value from the Cookie header
let session_token = parts
@ -44,11 +57,7 @@ impl FromRequestParts<AppState> for AuthUser {
.get_all(header::COOKIE)
.iter()
.filter_map(|v| v.to_str().ok())
.flat_map(|s| s.split(';'))
.map(|s| s.trim())
.find(|s| s.starts_with(&cookie_prefix))
.and_then(|s| s.strip_prefix(&cookie_prefix))
.map(|s| s.to_string());
.find_map(|header_value| extract_session_token(header_value, SESSION_COOKIE_NAME));
async move {
let session_token = session_token
@ -102,3 +111,50 @@ impl FromRequestParts<AppState> for AdminUser {
}
}
}
#[cfg(test)]
mod tests {
use super::extract_session_token;
use crate::services::auth::SESSION_COOKIE_NAME;
#[test]
fn test_valid_session_cookie_extracted() {
let header = format!("{}=abc123def", SESSION_COOKIE_NAME);
assert_eq!(
extract_session_token(&header, SESSION_COOKIE_NAME),
Some("abc123def".to_string())
);
}
#[test]
fn test_missing_session_cookie_returns_none() {
let header = "other=value; another=thing";
assert_eq!(extract_session_token(header, SESSION_COOKIE_NAME), None);
}
#[test]
fn test_multiple_cookies_correct_one_picked() {
let header = format!("theme=dark; {}=mytoken123; lang=fr", SESSION_COOKIE_NAME);
assert_eq!(
extract_session_token(&header, SESSION_COOKIE_NAME),
Some("mytoken123".to_string())
);
}
#[test]
fn test_cookie_with_whitespace_trimmed() {
// The whole segment is trimmed before matching, so leading/trailing
// whitespace around name=value pairs is stripped. The value itself
// also has its trailing spaces removed as part of that segment trim.
let header = format!(" {}=abc123 ; other=val ", SESSION_COOKIE_NAME);
assert_eq!(
extract_session_token(&header, SESSION_COOKIE_NAME),
Some("abc123".to_string())
);
}
#[test]
fn test_empty_cookie_header_returns_none() {
assert_eq!(extract_session_token("", SESSION_COOKIE_NAME), None);
}
}

Loading…
Cancel
Save