async-session-r2d2 0.1.3

Provides session management using r2d2 for async-session.
Documentation
use crate::sqlite::SessionStore as Store;
use crate::TableOperations;
use async_session::{chrono::Utc, serde_json, Session, SessionStore};
use r2d2_sqlite::rusqlite;
use std::time::Duration;

async fn test_store() -> anyhow::Result<Store> {
    Store::from_memory("testing".to_string())
}

#[async_std::test]
async fn creating_a_new_session_with_no_expiry() -> async_session::Result {
    let store = test_store().await?;
    let mut session = Session::new();
    session.insert("key", "value")?;
    let cloned = session.clone();
    let cookie_value = store.store_session(session).await?.unwrap();

    let (id, expires, serialized, count): (String, Option<i64>, String, i64) =
        store.connection()?.query_row(
            "select id, expires, session, count(*) from testing",
            rusqlite::params![],
            |row| {
                Ok((
                    row.get("id")?,
                    row.get("expires")?,
                    row.get("session")?,
                    row.get("count(*)")?,
                ))
            },
        )?;

    assert_eq!(1, count);
    assert_eq!(id, cloned.id());
    assert_eq!(expires, None);

    let deserialized_session: Session = serde_json::from_str(&serialized)?;
    assert_eq!(cloned.id(), deserialized_session.id());
    assert_eq!("value", &deserialized_session.get::<String>("key").unwrap());

    let loaded_session = store.load_session(cookie_value).await?.unwrap();
    assert_eq!(cloned.id(), loaded_session.id());
    assert_eq!("value", &loaded_session.get::<String>("key").unwrap());

    assert!(!loaded_session.is_expired());

    store.clear_store().await
}

#[async_std::test]
async fn updating_a_session() -> async_session::Result {
    let store = test_store().await?;
    let mut session = Session::new();
    let original_id = session.id().to_owned();

    session.insert("key", "value")?;
    let cookie_value = store.store_session(session).await?.unwrap();

    let mut session = store.load_session(cookie_value.clone()).await?.unwrap();
    session.insert("key", "other value")?;
    assert_eq!(None, store.store_session(session).await?);

    let session = store.load_session(cookie_value.clone()).await?.unwrap();
    assert_eq!(session.get::<String>("key").unwrap(), "other value");

    let (id, count): (String, i64) = store.connection()?.query_row(
        "select id, count(*) from testing",
        rusqlite::params![],
        |row| Ok((row.get("id")?, row.get("count(*)")?)),
    )?;

    assert_eq!(1, count);
    assert_eq!(original_id, id);

    store.clear_store().await
}

#[async_std::test]
async fn updating_a_session_extending_expiry() -> async_session::Result {
    let store = test_store().await?;
    let mut session = Session::new();
    session.expire_in(Duration::from_secs(10));
    let original_id = session.id().to_owned();
    let original_expires = session.expiry().unwrap().clone();
    let cookie_value = store.store_session(session).await?.unwrap();

    let mut session = store.load_session(cookie_value.clone()).await?.unwrap();
    assert_eq!(session.expiry().unwrap(), &original_expires);
    session.expire_in(Duration::from_secs(20));
    let new_expires = session.expiry().unwrap().clone();
    store.store_session(session).await?;

    let session = store.load_session(cookie_value.clone()).await?.unwrap();
    assert_eq!(session.expiry().unwrap(), &new_expires);

    let (id, expires, count): (String, i64, i64) = store.connection()?.query_row(
        "select id, expires, count(*) from testing",
        rusqlite::params![],
        |row| Ok((row.get("id")?, row.get("expires")?, row.get("count(*)")?)),
    )?;

    assert_eq!(1, count);
    assert_eq!(expires, new_expires.timestamp());
    assert_eq!(original_id, id);

    store.clear_store().await
}

#[async_std::test]
async fn creating_a_new_session_with_expiry() -> async_session::Result {
    let store = test_store().await?;
    let mut session = Session::new();
    session.expire_in(Duration::from_secs(1));
    session.insert("key", "value")?;
    let cloned = session.clone();

    let cookie_value = store.store_session(session).await?.unwrap();
    let (id, expires, serialized, count): (String, Option<i64>, String, i64) =
        store.connection()?.query_row(
            "select id, expires, session, count(*) from testing",
            rusqlite::params![],
            |row| {
                Ok((
                    row.get("id")?,
                    row.get("expires")?,
                    row.get("session")?,
                    row.get("count(*)")?,
                ))
            },
        )?;

    assert_eq!(1, count);
    assert_eq!(id, cloned.id());
    assert!(expires.unwrap() > Utc::now().timestamp());

    let deserialized_session: Session = serde_json::from_str(&serialized)?;
    assert_eq!(cloned.id(), deserialized_session.id());
    assert_eq!("value", &deserialized_session.get::<String>("key").unwrap());

    let loaded_session = store.load_session(cookie_value.clone()).await?.unwrap();
    assert_eq!(cloned.id(), loaded_session.id());
    assert_eq!("value", &loaded_session.get::<String>("key").unwrap());

    assert!(!loaded_session.is_expired());

    async_std::task::sleep(Duration::from_secs(1)).await;
    assert_eq!(None, store.load_session(cookie_value).await?);

    store.clear_store().await
}

#[async_std::test]
async fn destroying_a_single_session() -> async_session::Result {
    let store = test_store().await?;
    for _ in 0..3i8 {
        store.store_session(Session::new()).await?;
    }

    let cookie = store.store_session(Session::new()).await?.unwrap();
    assert_eq!(4, store.count()?);
    let session = store.load_session(cookie.clone()).await?.unwrap();
    store.destroy_session(session.clone()).await.unwrap();
    assert_eq!(None, store.load_session(cookie).await?);
    assert_eq!(3, store.count()?);

    // // attempting to destroy the session again is not an error
    // assert!(store.destroy_session(session).await.is_ok());
    store.clear_store().await
}

#[async_std::test]
async fn clearing_the_whole_store() -> async_session::Result {
    let store = test_store().await?;
    for _ in 0..3i8 {
        store.store_session(Session::new()).await?;
    }

    assert_eq!(3, store.count()?);
    store.clear_store().await.unwrap();
    assert_eq!(0, store.count()?);

    store.clear_store().await
}