idb 0.6.5

A futures based crate for interacting with IndexedDB on browsers using webassembly
Documentation
use idb::{DatabaseEvent, Factory, ObjectStoreParams, TransactionMode, TransactionResult};
use wasm_bindgen_test::wasm_bindgen_test;

#[wasm_bindgen_test]
async fn test_transaction_commit() {
    let factory = Factory::new().unwrap();
    factory.delete("test").unwrap().await.unwrap();

    let mut open_request = factory.open("test", Some(1)).unwrap();
    open_request.on_upgrade_needed(|event| {
        let database = event.database().unwrap();

        database
            .create_object_store("store1", ObjectStoreParams::new())
            .unwrap();
    });

    let database = open_request.await.unwrap();

    let transaction = database
        .transaction(&["store1"], TransactionMode::ReadWrite)
        .unwrap();

    let store = transaction.object_store("store1").unwrap();
    let id = store
        .add(
            &serde_wasm_bindgen::to_value("hello").unwrap(),
            Some(&serde_wasm_bindgen::to_value("world").unwrap()),
        )
        .unwrap()
        .await;

    assert!(id.is_ok(), "id should be ok: {}", id.unwrap_err());
    let id = id.unwrap();
    assert_eq!(serde_wasm_bindgen::to_value("world").unwrap(), id);

    let commit = transaction.commit().unwrap().await;
    assert!(
        commit.is_ok(),
        "commit should be ok: {}",
        commit.unwrap_err()
    );
    let commit = commit.unwrap();
    assert!(matches!(commit, TransactionResult::Committed));

    let read_transaction = database
        .transaction(&["store1"], TransactionMode::ReadOnly)
        .unwrap();
    let read_store = read_transaction.object_store("store1").unwrap();

    let count = read_store.count(None).unwrap().await;
    assert!(count.is_ok(), "count should be ok: {}", count.unwrap_err());
    let count = count.unwrap();

    assert_eq!(count, 1);

    let value = read_store
        .get(serde_wasm_bindgen::to_value("world").unwrap())
        .unwrap()
        .await;
    assert!(value.is_ok(), "value should be ok: {}", value.unwrap_err());
    let value = value.unwrap().unwrap();

    assert_eq!(value, serde_wasm_bindgen::to_value("hello").unwrap());

    database.close();
    factory.delete("test").unwrap().await.unwrap();
}

#[wasm_bindgen_test]
async fn test_transaction_abort() {
    let factory = Factory::new().unwrap();
    factory.delete("test").unwrap().await.unwrap();

    let mut open_request = factory.open("test", Some(1)).unwrap();
    open_request.on_upgrade_needed(|event| {
        let database = event.database().unwrap();

        database
            .create_object_store("store1", ObjectStoreParams::new())
            .unwrap();
    });

    let database = open_request.await.unwrap();

    let transaction = database
        .transaction(&["store1"], TransactionMode::ReadWrite)
        .unwrap();

    let store = transaction.object_store("store1").unwrap();
    let id = store
        .add(
            &serde_wasm_bindgen::to_value("hello").unwrap(),
            Some(&serde_wasm_bindgen::to_value("world").unwrap()),
        )
        .unwrap()
        .await;

    assert!(id.is_ok(), "id should be ok: {}", id.unwrap_err());
    let id = id.unwrap();
    assert_eq!(serde_wasm_bindgen::to_value("world").unwrap(), id);

    let abort = transaction.abort().unwrap().await;
    assert!(abort.is_ok(), "abort should be ok: {}", abort.unwrap_err());
    let abort = abort.unwrap();
    assert!(matches!(abort, TransactionResult::Aborted));

    let read_transaction = database
        .transaction(&["store1"], TransactionMode::ReadOnly)
        .unwrap();
    let read_store = read_transaction.object_store("store1").unwrap();

    let count = read_store.count(None).unwrap().await;
    assert!(count.is_ok(), "count should be ok: {}", count.unwrap_err());
    let count = count.unwrap();

    assert_eq!(count, 0);

    database.close();
    factory.delete("test").unwrap().await.unwrap();
}

#[wasm_bindgen_test]
async fn test_transaction_error() {
    let factory = Factory::new().unwrap();
    factory.delete("test").unwrap().await.unwrap();

    let mut open_request = factory.open("test", Some(1)).unwrap();
    open_request.on_upgrade_needed(|event| {
        let database = event.database().unwrap();

        database
            .create_object_store("store1", ObjectStoreParams::new())
            .unwrap();
    });

    let mut database = open_request.await.unwrap();
    database.on_version_change(|event| {
        event.database().unwrap().close();
    });

    let transaction = database
        .transaction(&["store1"], TransactionMode::ReadWrite)
        .unwrap();

    let store = transaction.object_store("store1").unwrap();
    store
        .add(
            &serde_wasm_bindgen::to_value("hello").unwrap(),
            Some(&serde_wasm_bindgen::to_value("world").unwrap()),
        )
        .unwrap()
        .await
        .unwrap();

    let mut open_request = factory.open("test", Some(2)).unwrap();
    open_request.on_upgrade_needed(|event| {
        let database = event.database().unwrap();

        database
            .create_object_store("store2", ObjectStoreParams::new())
            .unwrap();
    });

    let database = open_request.await.unwrap();

    let error = transaction.commit();
    assert!(error.is_err(), "commit should fail"); // Note that the value may still get committed to the store depending on indexedDB implementation on the browser

    database.close();
    factory.delete("test").unwrap().await.unwrap();
}

#[wasm_bindgen_test]
async fn test_transaction_read_write_in_loop() {
    let factory = Factory::new().unwrap();
    factory.delete("test").unwrap().await.unwrap();

    let mut open_request = factory.open("test", Some(1)).unwrap();
    open_request.on_upgrade_needed(|event| {
        let database = event.database().unwrap();

        database
            .create_object_store("store1", ObjectStoreParams::new())
            .unwrap();
    });

    let database = open_request.await.unwrap();

    // 1. Insert 10 values in database

    let transaction = database
        .transaction(&["store1"], TransactionMode::ReadWrite)
        .unwrap();

    let store = transaction.object_store("store1").unwrap();

    for i in 0..10 {
        let id = store
            .add(
                &serde_wasm_bindgen::to_value("hello").unwrap(),
                Some(&serde_wasm_bindgen::to_value(&i.to_string()).unwrap()),
            )
            .unwrap()
            .await;

        assert!(id.is_ok(), "id should be ok: {}", id.unwrap_err());
        let id = id.unwrap();
        assert_eq!(serde_wasm_bindgen::to_value(&i.to_string()).unwrap(), id);
    }

    let commit = transaction.commit().unwrap().await;
    assert!(
        commit.is_ok(),
        "commit should be ok: {}",
        commit.unwrap_err()
    );

    // 2. Change all the values in database

    let transaction = database
        .transaction(&["store1"], TransactionMode::ReadWrite)
        .unwrap();

    let store = transaction.object_store("store1").unwrap();

    for i in 0..10 {
        let value = store
            .get(serde_wasm_bindgen::to_value(&i.to_string()).unwrap())
            .unwrap()
            .await;

        assert!(value.is_ok(), "value should be ok: {}", value.unwrap_err());
        let value = value.unwrap();

        assert!(value.is_some(), "value should be some");
        let value = value.unwrap();
        assert_eq!(value, serde_wasm_bindgen::to_value("hello").unwrap());

        let id = store
            .put(
                &serde_wasm_bindgen::to_value("hello").unwrap(),
                Some(&serde_wasm_bindgen::to_value(&i.to_string()).unwrap()),
            )
            .unwrap()
            .await;

        assert!(id.is_ok(), "id should be ok: {}", id.unwrap_err());
        let id = id.unwrap();
        assert_eq!(serde_wasm_bindgen::to_value(&i.to_string()).unwrap(), id);
    }

    transaction.commit().unwrap().await.unwrap();

    database.close();
    factory.delete("test").unwrap().await.unwrap();
}

#[wasm_bindgen_test]
async fn test_transaction_fail_event_loop_control() {
    let factory = Factory::new().unwrap();
    factory.delete("test").unwrap().await.unwrap();

    let mut open_request = factory.open("test", Some(1)).unwrap();
    open_request.on_upgrade_needed(|event| {
        let database = event.database().unwrap();

        database
            .create_object_store("store1", ObjectStoreParams::new())
            .unwrap();
    });

    let database = open_request.await.unwrap();

    // 1. Insert values in database

    let transaction = database
        .transaction(&["store1"], TransactionMode::ReadWrite)
        .unwrap();

    let store = transaction.object_store("store1").unwrap();

    let id1 = store
        .add(
            &serde_wasm_bindgen::to_value("hello").unwrap(),
            Some(&serde_wasm_bindgen::to_value(&0.to_string()).unwrap()),
        )
        .unwrap()
        .await;

    assert!(id1.is_ok(), "id1 should be ok: {}", id1.unwrap_err());

    gloo::timers::future::TimeoutFuture::new(50).await; // Gives the control back to event loop so that indexed db tries to auto-commit the transaction

    let id2 = store.add(
        &serde_wasm_bindgen::to_value("hello").unwrap(),
        Some(&serde_wasm_bindgen::to_value(&1.to_string()).unwrap()),
    );

    assert!(id2.is_err(), "id2 should be err: {:?}", id2);

    database.close();
    factory.delete("test").unwrap().await.unwrap();
}