use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RefreshTokenData {
pub user_data: serde_json::Value,
pub expiry: DateTime<Utc>,
pub created: DateTime<Utc>,
}
impl RefreshTokenData {
pub fn is_expired(&self) -> bool {
Utc::now() > self.expiry
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Token {
pub access_token: String,
pub token_type: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub refresh_token: Option<String>,
pub expires_at: i64,
pub created_at: i64,
}
impl Token {
pub fn expires_in(&self) -> i64 {
self.expires_at - Utc::now().timestamp()
}
}
#[cfg(test)]
mod tests {
use super::*;
use chrono::Duration;
#[test]
fn test_token_fields() {
let now = Utc::now();
let expires_at = (now + Duration::hours(1)).timestamp();
let created_at = now.timestamp();
let token = Token {
access_token: "test.access.token".to_string(),
token_type: "Bearer".to_string(),
refresh_token: Some("test-refresh-token".to_string()),
expires_at,
created_at,
};
assert_eq!(token.access_token, "test.access.token");
assert_eq!(token.token_type, "Bearer");
assert_eq!(token.refresh_token, Some("test-refresh-token".to_string()));
assert_eq!(token.expires_at, expires_at);
assert_eq!(token.created_at, created_at);
}
#[test]
fn test_token_expires_in() {
let future_token = Token {
access_token: String::new(),
token_type: String::new(),
refresh_token: None,
expires_at: (Utc::now() + Duration::minutes(30)).timestamp(),
created_at: Utc::now().timestamp(),
};
let expires_in = future_token.expires_in();
assert!(
(expires_in - 1800).abs() <= 5,
"Future expiry: expires_in={}, expected ~1800",
expires_in
);
let past_token = Token {
access_token: String::new(),
token_type: String::new(),
refresh_token: None,
expires_at: (Utc::now() - Duration::minutes(30)).timestamp(),
created_at: Utc::now().timestamp(),
};
let expires_in = past_token.expires_in();
assert!(
(expires_in + 1800).abs() <= 5,
"Past expiry: expires_in={}, expected ~-1800",
expires_in
);
}
#[test]
fn test_refresh_token_data_is_expired() {
let valid = RefreshTokenData {
user_data: serde_json::json!({"user_id": "abc"}),
expiry: Utc::now() + Duration::hours(1),
created: Utc::now(),
};
assert!(
!valid.is_expired(),
"Token with future expiry should not be expired"
);
let expired = RefreshTokenData {
user_data: serde_json::json!({"user_id": "abc"}),
expiry: Utc::now() - Duration::hours(1),
created: Utc::now() - Duration::hours(2),
};
assert!(
expired.is_expired(),
"Token with past expiry should be expired"
);
}
}