asknothingx2-util 0.5.0

asknothing
Documentation
use std::time::Duration;

use asknothingx2_util::oauth::signed_token::{
    TokenConfig, TokenError, current_timestamp, extract_datetime, extract_timestamp, generate,
    generate_secret_key, is_expired, token_age, verify, verify_with_config,
};
use chrono::Utc;

#[tokio::test]
async fn basic_validation() {
    let secret_key = generate_secret_key();
    let token = generate(&secret_key, Some("user123"));

    assert!(verify(&secret_key, &token, Some("user123"), 3600).is_ok());
    assert!(verify(&secret_key, &token, Some("user456"), 3600).is_err());

    tokio::time::sleep(Duration::from_secs(1)).await;
    assert!(verify(&secret_key, &token, Some("user123"), 0).is_err());
}

#[tokio::test]
async fn clock_skew_tolerance() {
    let secret_key = generate_secret_key();
    let token = generate(&secret_key, Some("user123"));

    assert!(
        verify_with_config(
            &secret_key,
            &token,
            Some("user123"),
            &TokenConfig::new(3, 3600)
        )
        .is_ok()
    );
    assert!(
        verify_with_config(
            &secret_key,
            &token,
            Some("user456"),
            &TokenConfig::new(3, 3600)
        )
        .is_err()
    );

    tokio::time::sleep(Duration::from_secs(1)).await;
    assert!(
        verify_with_config(
            &secret_key,
            &token,
            Some("user123"),
            &TokenConfig::new(3, 0)
        )
        .is_ok()
    );
}

#[test]
fn test_extract_timestamp() {
    let current_time = current_timestamp();
    let secret_key = generate_secret_key();
    let token = generate(&secret_key, None);

    let extracted = extract_timestamp(&token);
    assert_eq!(current_time, extracted.unwrap());
}

#[test]
fn verify_token_with_errors() {
    let secret_key = generate_secret_key();
    let token = generate(&secret_key, Some("user123"));

    assert!(
        verify_with_config(
            &secret_key,
            &token,
            Some("user123"),
            &TokenConfig::default()
        )
        .is_ok()
    );

    assert_eq!(
        verify_with_config(
            &secret_key,
            &token,
            Some("invalid"),
            &TokenConfig::default()
        ),
        Err(TokenError::InvalidSignature)
    );

    assert_eq!(
        verify_with_config(
            &secret_key,
            &token,
            Some("user123"),
            &TokenConfig::new(0, 0)
        ),
        Err(TokenError::Expired)
    );

    assert_eq!(
        verify_with_config(
            &secret_key,
            "invalid_token",
            Some("user123"),
            &TokenConfig::default()
        ),
        Err(TokenError::InvalidFormat)
    );
}

#[test]
fn token_without_context() {
    let secret_key = generate_secret_key();
    let token = generate(&secret_key, None);

    assert!(verify(&secret_key, &token, None, 3600).is_ok());
    assert!(verify(&secret_key, &token, Some("user123"), 3600).is_err());
}

#[test]
fn test_token_age() {
    let secret_key = generate_secret_key();
    let token = generate(&secret_key, None);

    let age = token_age(&token).unwrap();
    assert!((0..2).contains(&age));
}

#[test]
fn test_is_expired() {
    let secret_key = generate_secret_key();
    let token = generate(&secret_key, None);

    assert_eq!(is_expired(&token, 3600), Ok(false));
    assert_eq!(is_expired(&token, 0), Ok(true));
    assert_eq!(is_expired("invalid", 3600), Err(TokenError::InvalidFormat));
}

#[test]
fn test_extract_datetime() {
    let secret_key = generate_secret_key();
    let token = generate(&secret_key, None);

    let dt = extract_datetime(&token).unwrap();
    let now = Utc::now();

    assert!((now.timestamp() - dt.timestamp()).abs() < 2);
}

#[test]
fn config_builder() {
    let config = TokenConfig::new(0, 1800);
    assert_eq!(config.clock_skew, None);
}