vantage-log-writer 0.5.3

Vantage extension for append-only log files (JSONL)
Documentation
use std::time::Duration;

use serde::{Deserialize, Serialize};
use tempfile::tempdir;
use tokio::time::sleep;
use vantage_dataset::traits::{InsertableDataSet, ReadableValueSet};
use vantage_log_writer::LogWriter;
use vantage_table::table::Table;
use vantage_types::EmptyEntity;

#[derive(Clone, Debug, Serialize, Deserialize)]
struct Event {
    name: String,
    user_id: i64,
    is_admin: bool,
    extra: String,
}

async fn read_lines(path: &std::path::Path) -> Vec<serde_json::Value> {
    let text = tokio::fs::read_to_string(path).await.unwrap();
    text.lines()
        .filter(|l| !l.trim().is_empty())
        .map(|l| serde_json::from_str(l).unwrap())
        .collect()
}

#[tokio::test]
async fn insert_writes_jsonl_line() {
    let dir = tempdir().unwrap();
    let writer = LogWriter::new(dir.path());
    let table = Table::<LogWriter, Event>::new("events", writer.clone())
        .with_column_of::<String>("name")
        .with_column_of::<i64>("user_id")
        .with_column_of::<bool>("is_admin");

    let event = Event {
        name: "login".into(),
        user_id: 42,
        is_admin: true,
        extra: "should be dropped".into(),
    };

    let id = table.insert_return_id(&event).await.unwrap();
    assert_eq!(id.len(), 26, "ULID is 26 chars: {}", id);

    sleep(Duration::from_millis(100)).await;

    let path = dir.path().join("events.jsonl");
    let rows = read_lines(&path).await;
    assert_eq!(rows.len(), 1);

    let row = &rows[0];
    assert_eq!(row["name"], "login");
    assert_eq!(row["user_id"], 42);
    assert_eq!(row["is_admin"], true);
    assert_eq!(row["id"], id);
    assert!(
        row.get("extra").is_none(),
        "fields not in column list must be dropped"
    );
}

#[tokio::test]
async fn cloned_table_shares_writer() {
    let dir = tempdir().unwrap();
    let writer = LogWriter::new(dir.path());
    let t1 = Table::<LogWriter, Event>::new("events", writer.clone())
        .with_column_of::<String>("name")
        .with_column_of::<i64>("user_id")
        .with_column_of::<bool>("is_admin");
    let t2 = t1.clone();

    let e = Event {
        name: "x".into(),
        user_id: 1,
        is_admin: false,
        extra: "".into(),
    };
    let _ = t1.insert_return_id(&e).await.unwrap();
    let _ = t2.insert_return_id(&e).await.unwrap();

    sleep(Duration::from_millis(100)).await;

    let rows = read_lines(&dir.path().join("events.jsonl")).await;
    assert_eq!(rows.len(), 2);
}

#[tokio::test]
async fn read_methods_return_unsupported() {
    let dir = tempdir().unwrap();
    let writer = LogWriter::new(dir.path());
    let table = Table::<LogWriter, EmptyEntity>::new("events", writer);

    let res = table.list_values().await;
    assert!(res.is_err());
    assert_eq!(
        res.unwrap_err().kind(),
        vantage_core::ErrorKind::Unsupported
    );

    let res = table.get_value(&"any".to_string()).await;
    assert!(res.is_err());
    assert_eq!(
        res.unwrap_err().kind(),
        vantage_core::ErrorKind::Unsupported
    );
}

#[tokio::test]
async fn insert_with_explicit_id_uses_it() {
    let dir = tempdir().unwrap();
    let writer = LogWriter::new(dir.path());
    let table =
        Table::<LogWriter, EmptyEntity>::new("events", writer).with_column_of::<String>("name");

    use vantage_dataset::traits::WritableValueSet;
    let mut record = vantage_types::Record::<serde_json::Value>::new();
    record.insert("name".into(), serde_json::Value::String("hello".into()));
    let stored = WritableValueSet::insert_value(&table, &"my-id".to_string(), &record)
        .await
        .unwrap();
    assert_eq!(stored["id"], "my-id");

    sleep(Duration::from_millis(100)).await;

    let rows = read_lines(&dir.path().join("events.jsonl")).await;
    assert_eq!(rows.len(), 1);
    assert_eq!(rows[0]["id"], "my-id");
    assert_eq!(rows[0]["name"], "hello");
}