rings-core 0.3.0

Chord DHT implementation with ICE
Documentation
use std::mem::size_of_val;

use rexie::TransactionMode;
use serde::Deserialize;
use serde::Serialize;
use serde_json::Value as JsonValue;
use wasm_bindgen::JsValue;
use wasm_bindgen_test::wasm_bindgen_test;

use crate::storage::persistence::idb::IDBStorageBasic;
use crate::storage::persistence::IDBStorage;
use crate::storage::persistence::PersistenceStorageOperation;
use crate::storage::persistence::PersistenceStorageReadAndWrite;
use crate::storage::persistence::PersistenceStorageRemove;

async fn create_db_instance(cap: usize) -> IDBStorage {
    let instance = IDBStorage::new_with_cap(cap).await.unwrap();
    let (tx, store) = instance.get_tx_store(TransactionMode::ReadWrite).unwrap();
    store.clear().await.unwrap();
    tx.done().await.unwrap();
    let (_tx, store) = instance.get_tx_store(TransactionMode::ReadOnly).unwrap();
    assert!(store.count(None).await.unwrap() == 0, "store not empty");
    instance
}

#[derive(Serialize, Deserialize, Debug)]
struct TestDataStruct {
    content: String,
}

#[wasm_bindgen_test]
async fn test_create_put_data() {
    let instance = create_db_instance(4).await;
    let key = "1".to_string();
    let value = TestDataStruct {
        content: "content1".to_string(),
    };
    instance.put(&key, &value).await.unwrap();

    let (_tx, store) = instance.get_tx_store(TransactionMode::ReadOnly).unwrap();
    assert!(store.count(None).await.unwrap() == 1, "indexedDB is empty");

    let real_value_1 = store.get(&JsValue::from(&key)).await.unwrap();
    #[allow(deprecated)]
    let real_value_1: JsonValue = real_value_1.into_serde().unwrap();
    let last_visit_1 = real_value_1
        .get("last_visit_time")
        .unwrap()
        .as_i64()
        .unwrap();

    assert!(
        real_value_1.get("visit_count").unwrap().as_i64().unwrap() == 0,
        "visit_count not zero"
    );
    let real_value_data_1: TestDataStruct =
        serde_json::from_value(real_value_1.get("data").unwrap().to_owned()).unwrap();
    assert!(
        real_value_data_1.content.eq(&value.content),
        "Data content in store not same: expect {}, got {}",
        value.content,
        real_value_data_1.content
    );

    let r: TestDataStruct = instance.get(&key).await.unwrap().unwrap();
    tracing::debug!("{:?}", r);
    assert_eq!(r.content, value.content);

    let (_tx, store) = instance.get_tx_store(TransactionMode::ReadOnly).unwrap();
    assert!(store.count(None).await.unwrap() == 1, "indexedDB is empty");
    let real_value_2 = store.get(&JsValue::from(&key)).await.unwrap();
    #[allow(deprecated)]
    let real_value_2: JsonValue = real_value_2.into_serde().unwrap();
    let last_visit_2 = real_value_2
        .get("last_visit_time")
        .unwrap()
        .as_i64()
        .unwrap();

    assert!(
        last_visit_1 != last_visit_2,
        "last_visit_1 and last_visit_2 is same, {}",
        last_visit_1
    );
    assert!(
        real_value_2.get("visit_count").unwrap().as_i64().unwrap() == 1,
        "2. visit_count not zero"
    );
    let real_value_data_2: TestDataStruct =
        serde_json::from_value(real_value_2.get("data").unwrap().to_owned()).unwrap();
    assert!(
        real_value_data_2.content.eq(&value.content),
        "2. Data content in store not same: expect {}, got {}",
        value.content,
        real_value_data_2.content
    );

    instance.clear().await.unwrap();
    let (_tx, store) = instance.get_tx_store(TransactionMode::ReadOnly).unwrap();
    assert!(
        store.count(None).await.unwrap() == 0,
        "indexedDB is not empty"
    );
}

#[wasm_bindgen_test]
async fn test_indexed_db_count() {
    let instance = create_db_instance(4).await;
    instance
        .put(&"1".to_string(), &serde_json::json!("test1"))
        .await
        .unwrap();
    instance
        .put(&"2".to_string(), &serde_json::json!("test2"))
        .await
        .unwrap();
    instance
        .put(&"3".to_string(), &serde_json::json!("test3"))
        .await
        .unwrap();
    instance
        .put(&"4".to_string(), &serde_json::json!("test4"))
        .await
        .unwrap();
    let count = instance.count().await.unwrap();
    assert!(count == 4, "count error, got: {:?}, expect: {:?}", count, 4);
    instance.clear().await.unwrap();
    let (_tx, store) = instance.get_tx_store(TransactionMode::ReadOnly).unwrap();
    assert!(
        store.count(None).await.unwrap() == 0,
        "indexedDB is not empty"
    );
}

#[wasm_bindgen_test]
async fn test_indexed_db_remove() {
    let instance = create_db_instance(4).await;
    let key1 = "1".to_string();
    let key2 = "2".to_string();
    let key3 = "3".to_string();
    let key4 = "4".to_string();
    instance
        .put(&key1, &serde_json::json!("test1"))
        .await
        .unwrap();
    instance
        .put(&key2, &serde_json::json!("test2"))
        .await
        .unwrap();
    instance
        .put(&key3, &serde_json::json!("test3"))
        .await
        .unwrap();
    instance
        .put(&key4, &serde_json::json!("test4"))
        .await
        .unwrap();
    let count = instance.count().await.unwrap();
    assert!(count == 4, "count error, got: {:?}, expect: {:?}", count, 4);

    instance.remove(&key1).await.unwrap();
    let count = instance.count().await.unwrap();
    assert!(count == 3, "count error, got: {:?}, expect: {:?}", count, 3);

    instance.clear().await.unwrap();
    assert!(
        instance.count().await.unwrap() == 0,
        "indexedDB is not empty"
    );
}

#[wasm_bindgen_test]
async fn test_idb_prune() {
    super::setup_log();
    let instance = create_db_instance(4).await;
    let key1 = "1".to_string();
    let key2 = "2".to_string();
    let key3 = "3".to_string();
    let key4 = "4".to_string();
    let key5 = "5".to_string();
    instance
        .put(&key1, &TestDataStruct {
            content: "test1".to_owned(),
        })
        .await
        .unwrap();
    instance
        .put(&key2, &TestDataStruct {
            content: "test2".to_owned(),
        })
        .await
        .unwrap();
    instance
        .put(&key3, &TestDataStruct {
            content: "test3".to_owned(),
        })
        .await
        .unwrap();
    instance
        .put(&key4, &TestDataStruct {
            content: "test4".to_owned(),
        })
        .await
        .unwrap();

    let d3: TestDataStruct = instance.get(&key3).await.unwrap().unwrap();
    tracing::debug!("d3, {:?}", d3);
    let d3: TestDataStruct = instance.get(&key3).await.unwrap().unwrap();
    tracing::debug!("d3, {:?}", d3);
    let d1: TestDataStruct = instance.get(&key1).await.unwrap().unwrap();
    tracing::debug!("d1, {:?}", d1);
    let d2: TestDataStruct = instance.get(&key2).await.unwrap().unwrap();
    tracing::debug!("d2, {:?}", d2);

    instance
        .put(&key5, &TestDataStruct {
            content: "test5".to_owned(),
        })
        .await
        .unwrap();

    let entries: Vec<(String, TestDataStruct)> = instance.get_all().await.unwrap();
    assert!(
        !entries.iter().any(|(k, _v)| k.eq(&key4)),
        "key4 should be deleted"
    );

    instance.clear().await.unwrap();
    assert!(
        instance.count().await.unwrap() == 0,
        "indexedDB is not empty"
    );
}

#[wasm_bindgen_test]
async fn test_idb_total_size() {
    let instance = create_db_instance(4).await;
    let key1 = "1".to_string();
    let value1 = TestDataStruct {
        content: "test1".to_owned(),
    };
    instance.put(&key1, &value1).await.unwrap();
    #[allow(deprecated)]
    let expect_size =
        size_of_val(&JsValue::from(key1)) + size_of_val(&JsValue::from_serde(&value1).unwrap());
    let total_size = instance.total_size().await.unwrap();
    assert!(total_size > 0, "total_size should > 0");
    assert!(
        total_size == expect_size,
        "total_size got: {}, expect: {}",
        total_size,
        expect_size
    );

    instance.clear().await.unwrap();
    assert!(
        instance.count().await.unwrap() == 0,
        "indexedDB is not empty"
    );
}