factstr-sqlite 0.4.1

Embedded SQLite store for FACTSTR with append, query, streams, and durable streams.
Documentation
mod support;

use factstr::{EventQuery, EventStore};
use factstr_sqlite::SqliteStore;

use support::{TemporaryDatabaseFile, connect, index_exists, metadata_value, table_exists};

#[test]
fn opening_a_new_database_creates_schema_and_metadata() {
    let database_file = TemporaryDatabaseFile::new("bootstrap-schema");

    let store =
        SqliteStore::open(database_file.path()).expect("sqlite store should open a new database");
    assert_eq!(store.database_path(), database_file.path());
    drop(store);

    let runtime = tokio::runtime::Builder::new_current_thread()
        .enable_all()
        .build()
        .expect("tokio runtime should build");

    runtime.block_on(async {
        let mut connection = connect(database_file.path()).await;

        assert!(table_exists(&mut connection, "events").await);
        assert!(table_exists(&mut connection, "store_metadata").await);
        assert!(index_exists(&mut connection, "idx_events_type").await);
        assert!(index_exists(&mut connection, "idx_events_occurred_at").await);

        let store_format_version = metadata_value(&mut connection, "store_format_version").await;
        assert_eq!(store_format_version.as_deref(), Some("1"));
    });
}

#[test]
fn opening_a_new_database_succeeds_inside_a_running_tokio_runtime() {
    let database_file = TemporaryDatabaseFile::new("bootstrap-tokio");
    let runtime = tokio::runtime::Builder::new_current_thread()
        .enable_all()
        .build()
        .expect("tokio runtime should build");

    runtime.block_on(async {
        let store = SqliteStore::open(database_file.path())
            .expect("sqlite store should open inside a running tokio runtime");
        assert_eq!(store.database_path(), database_file.path());
    });

    runtime.block_on(async {
        let mut connection = connect(database_file.path()).await;

        assert!(table_exists(&mut connection, "events").await);
        assert!(table_exists(&mut connection, "store_metadata").await);

        let store_format_version = metadata_value(&mut connection, "store_format_version").await;
        assert_eq!(store_format_version.as_deref(), Some("1"));
    });
}

#[test]
fn reopening_the_same_database_succeeds_and_preserves_metadata() {
    let database_file = TemporaryDatabaseFile::new("bootstrap-reopen");

    SqliteStore::open(database_file.path()).expect("first open should succeed");
    SqliteStore::open(database_file.path()).expect("second open should succeed");

    let runtime = tokio::runtime::Builder::new_current_thread()
        .enable_all()
        .build()
        .expect("tokio runtime should build");

    runtime.block_on(async {
        let mut connection = connect(database_file.path()).await;
        let store_format_version = metadata_value(&mut connection, "store_format_version").await;
        assert_eq!(store_format_version.as_deref(), Some("1"));
    });
}

#[test]
fn wal_mode_is_active_after_bootstrap() {
    let database_file = TemporaryDatabaseFile::new("bootstrap-wal");

    SqliteStore::open(database_file.path()).expect("sqlite store should open a new database");

    let runtime = tokio::runtime::Builder::new_current_thread()
        .enable_all()
        .build()
        .expect("tokio runtime should build");

    runtime.block_on(async {
        let mut connection = connect(database_file.path()).await;
        let journal_mode = sqlx::query_scalar::<_, String>("PRAGMA journal_mode")
            .fetch_one(&mut connection)
            .await
            .expect("journal_mode pragma should be readable");

        assert_eq!(journal_mode.to_lowercase(), "wal");
    });
}

#[test]
fn no_runtime_methods_remain_deferred_after_phase_d() {
    let database_file = TemporaryDatabaseFile::new("bootstrap-trait");
    let store = SqliteStore::open(database_file.path()).expect("sqlite store should open");

    let event_store: &dyn EventStore = &store;

    let _subscription = event_store
        .stream_all(std::sync::Arc::new(|_| Ok(())))
        .expect("subscribe_all should succeed");
    let _filtered_subscription = event_store
        .stream_to(&EventQuery::all(), std::sync::Arc::new(|_| Ok(())))
        .expect("subscribe_to should succeed");
}