artisan_keystore 2.1.1

A keystore server designed for AH
Documentation
use ais_keystore_lib::key_store::{key::KeyStatus, store::{generate_new_key, KeyStore}};
use dusa_collection_utils::core::types::pathtype::PathType;
use std::fs;

#[tokio::test]
async fn test_add_and_get_key() {
    // initialise store with empty state
    let mut store = KeyStore::new().await;
    // override file path to avoid writing to system paths
    store.file_path = PathType::Str("/tmp/test_keystore.json".into());
    let _ = fs::remove_file("/tmp/test_keystore.json");

    store.add_key("alpha".into(), vec![1, 2, 3], Some(60));
    assert!(store.keys.contains_key("alpha"));

    let key = store.get_key("alpha", None).expect("key should be present");
    assert_eq!(key.id, "alpha");
    assert_eq!(key.value, vec![1, 2, 3]);
    assert_eq!(key.version, 1);
}

#[tokio::test]
async fn test_migrate_key() {
    let mut store = KeyStore::new().await;
    store.file_path = PathType::Str("/tmp/test_keystore.json".into());
    store.add_key("beta".into(), vec![9, 9, 9], Some(60));

    // mark key as near expiration to allow migration
    if let Some(key) = store.keys.get_mut("beta") {
        key.status = KeyStatus::NearExpiration;
    }
    let old_version = store.keys.get("beta").unwrap().version;

    store.migrate_key("beta");

    let new_version = store.keys.get("beta").unwrap().version;
    assert_eq!(new_version, old_version + 1);
    let archived = store.archived_keys.get("beta").expect("should archive old version");
    assert_eq!(archived.len(), 1);
}

#[tokio::test]
async fn test_purge_old_archives() {
    let mut store = KeyStore::new().await;
    store.file_path = PathType::Str("/tmp/test_keystore.json".into());

    // create an archived key that is older than retention
    let old_key = ais_keystore_lib::key_store::key::Key {
        id: "gamma".into(),
        value: vec![0],
        legnth: 1,
        ttl: 60,
        created_at: 0,
        expires_at: 0,
        version: 1,
        status: KeyStatus::Expired,
    };
    store.archived_keys.insert("gamma".into(), vec![old_key]);

    store.purge_old_archives(1); // 1 day retention
    assert!(store.archived_keys.get("gamma").unwrap().is_empty());
}

#[tokio::test]
async fn test_rotate_expiring_keys() {
    let mut store = KeyStore::new().await;
    store.file_path = PathType::Str("/tmp/test_keystore.json".into());

    // due for rotation: already expired
    store.add_key("expiring".into(), vec![1, 2, 3], Some(60));
    if let Some(key) = store.keys.get_mut("expiring") {
        key.expires_at = 0;
    }

    // not due: expires far in the future
    store.add_key("fresh".into(), vec![4, 5, 6], Some(60 * 60 * 24 * 365));

    let rotated = store.rotate_expiring_keys();

    assert_eq!(rotated, vec!["expiring".to_string()]);
    assert_eq!(store.keys.get("expiring").unwrap().version, 2);
    assert_eq!(store.keys.get("fresh").unwrap().version, 1);
    assert!(store.archived_keys.contains_key("expiring"));
    assert!(!store.archived_keys.contains_key("fresh"));
}

#[test]
fn test_generate_new_key_length() {
    let key = generate_new_key(Some(64));
    assert_eq!(key.len(), 64);
}