eventuary-sqlite 0.2.0

SQLite event backend for eventuary
Documentation
use chrono::Utc;

use eventuary_core::io::handler::{MultiplexerKey, MultiplexerStore, SubscriberId};
use eventuary_core::io::reader::{BufferStore, DedupeStore, WatermarkStore};
use eventuary_core::{Event, EventId, Payload};
use eventuary_sqlite::buffer::{SqliteBufferStore, SqliteBufferStoreConfig};
use eventuary_sqlite::database::{SqliteConn, SqliteDatabase};
use eventuary_sqlite::dedupe::{SqliteDedupeStore, SqliteDedupeStoreConfig};
use eventuary_sqlite::multiplexer::{SqliteMultiplexerStore, SqliteMultiplexerStoreConfig};
use eventuary_sqlite::watermark::{SqliteWatermarkStore, SqliteWatermarkStoreConfig};

fn prepare_test_schema(conn: &SqliteConn) {
    SqliteMultiplexerStore::prepare_schema(conn, &SqliteMultiplexerStoreConfig::default()).unwrap();
    SqliteDedupeStore::prepare_schema(conn, &SqliteDedupeStoreConfig::default()).unwrap();
    SqliteBufferStore::<i64>::prepare_schema(conn, &SqliteBufferStoreConfig::default()).unwrap();
    SqliteWatermarkStore::prepare_schema(conn, &SqliteWatermarkStoreConfig::default()).unwrap();
}

fn ev(topic: &str) -> Event {
    Event::builder("acme", "/x", topic, "k", Payload::from_string("p"))
        .unwrap()
        .build()
        .unwrap()
}

#[tokio::test]
async fn sqlite_multiplexer_store_records_and_skips_completed() {
    let db = SqliteDatabase::open_in_memory().unwrap();
    prepare_test_schema(&db.conn());
    let store = SqliteMultiplexerStore::new(db.conn(), SqliteMultiplexerStoreConfig::default());
    let key = MultiplexerKey::new(EventId::new(), SubscriberId::new("audit").unwrap());

    assert!(!store.is_completed(&key).await.unwrap());
    store.mark_completed(&key).await.unwrap();
    assert!(store.is_completed(&key).await.unwrap());

    store.mark_completed(&key).await.unwrap();
    assert!(store.is_completed(&key).await.unwrap());
}

#[tokio::test]
async fn sqlite_multiplexer_store_scopes_by_subscriber() {
    let db = SqliteDatabase::open_in_memory().unwrap();
    prepare_test_schema(&db.conn());
    let store = SqliteMultiplexerStore::new(db.conn(), SqliteMultiplexerStoreConfig::default());
    let event_id = EventId::new();
    let k1 = MultiplexerKey::new(event_id, SubscriberId::new("a").unwrap());
    let k2 = MultiplexerKey::new(event_id, SubscriberId::new("b").unwrap());

    store.mark_completed(&k1).await.unwrap();
    assert!(store.is_completed(&k1).await.unwrap());
    assert!(!store.is_completed(&k2).await.unwrap());
}

#[tokio::test]
async fn sqlite_dedupe_store_marks_and_finds_events() {
    let db = SqliteDatabase::open_in_memory().unwrap();
    prepare_test_schema(&db.conn());
    let store = SqliteDedupeStore::new(db.conn(), SqliteDedupeStoreConfig::default());
    let event = ev("t1");

    assert!(!store.exists(&event).await.unwrap());
    store.mark_processed(&event).await.unwrap();
    assert!(store.exists(&event).await.unwrap());
}

#[tokio::test]
async fn sqlite_dedupe_store_mark_if_new_returns_false_on_duplicate() {
    let db = SqliteDatabase::open_in_memory().unwrap();
    prepare_test_schema(&db.conn());
    let store = SqliteDedupeStore::new(db.conn(), SqliteDedupeStoreConfig::default());
    let event = ev("t1");

    assert!(store.mark_if_new(&event).await.unwrap());
    assert!(!store.mark_if_new(&event).await.unwrap());
}

#[tokio::test]
async fn sqlite_buffer_store_round_trips_entry() {
    let db = SqliteDatabase::open_in_memory().unwrap();
    prepare_test_schema(&db.conn());
    let store: SqliteBufferStore<i64> =
        SqliteBufferStore::new(db.conn(), SqliteBufferStoreConfig::default());
    let event = ev("t1");
    let cursor: i64 = 42;

    let id = store.push(&event, &cursor).await.unwrap();
    let pending = store.pending().await.unwrap();
    assert_eq!(pending.len(), 1);
    assert_eq!(pending[0].id, id);
    assert_eq!(pending[0].event.id(), event.id());
    assert_eq!(pending[0].cursor, cursor);

    store.ack(&id).await.unwrap();
    let pending_after = store.pending().await.unwrap();
    assert!(pending_after.is_empty());
}

#[tokio::test]
async fn sqlite_buffer_store_nack_keeps_entry() {
    let db = SqliteDatabase::open_in_memory().unwrap();
    prepare_test_schema(&db.conn());
    let store: SqliteBufferStore<i64> =
        SqliteBufferStore::new(db.conn(), SqliteBufferStoreConfig::default());
    let id = store.push(&ev("t1"), &1_i64).await.unwrap();
    store.nack(&id).await.unwrap();
    let pending = store.pending().await.unwrap();
    assert_eq!(pending.len(), 1);
}

#[tokio::test]
async fn sqlite_buffer_store_pending_orders_by_id() {
    let db = SqliteDatabase::open_in_memory().unwrap();
    prepare_test_schema(&db.conn());
    let store: SqliteBufferStore<i64> =
        SqliteBufferStore::new(db.conn(), SqliteBufferStoreConfig::default());
    let id1 = store.push(&ev("t1"), &1_i64).await.unwrap();
    let id2 = store.push(&ev("t2"), &2_i64).await.unwrap();
    let id3 = store.push(&ev("t3"), &3_i64).await.unwrap();

    let pending = store.pending().await.unwrap();
    assert_eq!(pending.len(), 3);
    assert_eq!(pending[0].id, id1);
    assert_eq!(pending[1].id, id2);
    assert_eq!(pending[2].id, id3);
}

#[tokio::test]
async fn sqlite_watermark_store_save_and_load() {
    let db = SqliteDatabase::open_in_memory().unwrap();
    prepare_test_schema(&db.conn());
    let store = SqliteWatermarkStore::new(db.conn(), SqliteWatermarkStoreConfig::default());
    assert!(store.load_watermark("k").await.unwrap().is_none());

    let now = Utc::now();
    store.save_watermark("k", now).await.unwrap();
    let loaded = store.load_watermark("k").await.unwrap().unwrap();
    assert_eq!(loaded.timestamp_millis(), now.timestamp_millis());
}

#[tokio::test]
async fn sqlite_watermark_store_save_overwrites() {
    let db = SqliteDatabase::open_in_memory().unwrap();
    prepare_test_schema(&db.conn());
    let store = SqliteWatermarkStore::new(db.conn(), SqliteWatermarkStoreConfig::default());
    let t1 = Utc::now();
    let t2 = t1 + chrono::Duration::seconds(60);
    store.save_watermark("k", t1).await.unwrap();
    store.save_watermark("k", t2).await.unwrap();
    let loaded = store.load_watermark("k").await.unwrap().unwrap();
    assert_eq!(loaded.timestamp_millis(), t2.timestamp_millis());
}

#[tokio::test]
async fn sqlite_watermark_store_scopes_by_key() {
    let db = SqliteDatabase::open_in_memory().unwrap();
    prepare_test_schema(&db.conn());
    let store = SqliteWatermarkStore::new(db.conn(), SqliteWatermarkStoreConfig::default());
    let now = Utc::now();
    store.save_watermark("a", now).await.unwrap();
    assert!(store.load_watermark("a").await.unwrap().is_some());
    assert!(store.load_watermark("b").await.unwrap().is_none());
}