lix 0.18.0

Embeddable version control for apps and AI agents.
Documentation
use lix::Value;

#[tokio::test]
async fn mainline_checkpoint_history_uses_actual_endpoints_and_keeps_empty_log_entries() {
    let lix = crate::open_lix().await.unwrap();
    lix.execute(
        "INSERT INTO lix_key_value (key, value) VALUES ('demo', 'one')",
        &[],
    )
    .await
    .unwrap();
    let checkpoint = lix.create_checkpoint().await.unwrap().commit_id;
    let empty = lix.create_checkpoint().await.unwrap().commit_id;
    let log = lix.execute("SELECT commit_id, parent_commit_id, is_checkpoint, position FROM lix_log() WHERE is_checkpoint ORDER BY position LIMIT 2", &[]).await.unwrap();
    assert_eq!(log.len(), 2);
    assert_eq!(log.rows()[0].get::<String>("commit_id").unwrap(), empty);
    assert_eq!(
        log.rows()[0].get::<String>("parent_commit_id").unwrap(),
        checkpoint
    );
    let history = lix.execute("SELECT key, diff_type, lixcol_to_commit_id FROM lix_log() l JOIN lix_history('lix_key_value') h ON h.lixcol_to_commit_id = l.commit_id WHERE key = 'demo' AND l.is_checkpoint ORDER BY l.position", &[]).await.unwrap();
    assert_eq!(history.len(), 1);
    assert_eq!(
        history.rows()[0]
            .get::<String>("lixcol_to_commit_id")
            .unwrap(),
        checkpoint
    );
    assert_eq!(
        history.rows()[0].get::<String>("diff_type").unwrap(),
        "added"
    );
    let page = lix.execute("WITH page AS (SELECT commit_id, position FROM lix_log($1) WHERE is_checkpoint ORDER BY position LIMIT 2) SELECT p.commit_id, h.key FROM page p LEFT JOIN lix_history('lix_key_value', $1) h ON h.lixcol_to_commit_id = p.commit_id ORDER BY p.position", &[Value::Text(empty)]).await.unwrap();
    assert_eq!(page.len(), 2);
    let state = lix
        .execute(
            "SELECT value FROM lix_as_of('lix_key_value', $1) WHERE key = 'demo'",
            &[Value::Text(checkpoint)],
        )
        .await
        .unwrap();
    assert_eq!(
        state.rows()[0].get::<serde_json::Value>("value").unwrap(),
        serde_json::json!("one")
    );
    lix.close().await.unwrap();
}

#[tokio::test]
async fn mainline_working_context_and_checkpoint_log() {
    let lix = crate::open_lix().await.unwrap();
    let initial = lix.execute("SELECT commit_id, working_base_commit_id FROM lix_branch WHERE id = lix_active_branch_id()", &[]).await.unwrap();
    assert_eq!(
        initial.rows()[0].get::<String>("commit_id").unwrap(),
        initial.rows()[0]
            .get::<String>("working_base_commit_id")
            .unwrap()
    );
    lix.execute(
        "INSERT INTO lix_key_value (key, value) VALUES ('demo', 'one')",
        &[],
    )
    .await
    .unwrap();
    let diff = lix.execute("SELECT lixcol_from_commit_id, lixcol_to_commit_id FROM lix_diff('lix_key_value') WHERE key = 'demo'", &[]).await.unwrap();
    assert_eq!(
        diff.rows()[0]
            .get::<String>("lixcol_from_commit_id")
            .unwrap(),
        initial.rows()[0]
            .get::<String>("working_base_commit_id")
            .unwrap()
    );
    let c = lix.create_checkpoint().await.unwrap().commit_id;
    let commits = lix
        .execute(
            "SELECT commit_id FROM lix_log() WHERE is_checkpoint AND commit_id = $1",
            &[Value::Text(c.clone())],
        )
        .await
        .unwrap();
    assert_eq!(commits.rows()[0].get::<String>("commit_id").unwrap(), c);
    assert!(
        lix.execute("SELECT * FROM lix_checkpoint", &[])
            .await
            .is_err()
    );
    assert!(
        lix.execute(
            "SELECT * FROM lix_state_at('lix_file', $1)",
            &[Value::Text(c)]
        )
        .await
        .is_err()
    );
    lix.close().await.unwrap();
}

#[tokio::test(flavor = "current_thread")]
async fn mainline_page_work_is_independent_of_older_retained_history() {
    let lix = crate::open_lix().await.unwrap();
    let mut ids = Vec::new();
    for index in 0..60 {
        lix.execute("INSERT INTO lix_key_value (key, value) VALUES ('profile', $1) ON CONFLICT (key) DO UPDATE SET value = excluded.value", &[Value::Text(index.to_string())]).await.unwrap();
        ids.push(lix.create_checkpoint().await.unwrap().commit_id);
        if ![9, 29, 59].contains(&index) {
            continue;
        }
        let anchor = ids.last().unwrap().clone();
        crate::sql2::take_mainline_work();
        let start = std::time::Instant::now();
        let log = lix
            .execute(
                "SELECT commit_id FROM lix_log($1) WHERE is_checkpoint ORDER BY position LIMIT 5",
                &[Value::Text(anchor.clone())],
            )
            .await
            .unwrap();
        let log_work = crate::sql2::take_mainline_work();
        assert_eq!(log.len(), 5);
        assert!(log_work.0 <= 6, "log must stop at its page: {log_work:?}");
        let page = lix.execute("WITH page AS (SELECT commit_id, position FROM lix_log($1) WHERE is_checkpoint ORDER BY position LIMIT 5) SELECT p.commit_id, h.key FROM page p LEFT JOIN lix_history('lix_key_value', $1) h ON h.lixcol_to_commit_id = p.commit_id ORDER BY p.position", &[Value::Text(anchor.clone())]).await.unwrap();
        let work = crate::sql2::take_mainline_work();
        assert_eq!(page.len(), 5);
        assert!(
            work.1 <= 5,
            "preview must diff only selected commits: {work:?}"
        );
        assert!(
            work.0 <= 12,
            "preview must not walk older history: {work:?}"
        );
        eprintln!(
            "mainline profile retained={} page=5 elapsed_us={} log_work={log_work:?} preview_work={work:?}",
            ids.len(),
            start.elapsed().as_micros()
        );
    }
    lix.close().await.unwrap();
}

#[tokio::test]
async fn checkpoint_status_has_one_public_home_and_is_pinned_to_log_anchor() {
    let lix = crate::open_lix().await.unwrap();
    lix.execute("INSERT INTO lix_key_value (key,value) VALUES ('anchor-test','one')", &[]).await.unwrap();
    let c = lix.create_checkpoint().await.unwrap().commit_id;
    let undone = lix.execute("SELECT commit_id FROM lix_undo($1)", &[Value::Text(c.clone())]).await.unwrap();
    let u = undone.rows()[0].get::<String>("commit_id").unwrap();
    let redone = lix.execute("SELECT commit_id FROM lix_redo($1)", &[Value::Text(u.clone())]).await.unwrap();
    let r = redone.rows()[0].get::<String>("commit_id").unwrap();
    for (anchor, active) in [(&c,true),(&u,false),(&r,true)] {
        let log = lix.execute("SELECT is_checkpoint FROM lix_log($1) WHERE commit_id=$2", &[Value::Text(anchor.clone()), Value::Text(c.clone())]).await.unwrap();
        assert_eq!(log.rows()[0].get::<bool>("is_checkpoint").unwrap(), active);
        let history = lix.execute("SELECT h.diff_type, h.lixcol_to_commit_id FROM lix_log($1) l JOIN lix_history('lix_key_value',$1) h ON h.lixcol_to_commit_id=l.commit_id WHERE l.is_checkpoint AND h.key='anchor-test'", &[Value::Text(anchor.clone())]).await.unwrap();
        assert_eq!(history.len(), usize::from(active));
        if active {
            assert_eq!(history.rows()[0].get::<String>("lixcol_to_commit_id").unwrap(), c);
            assert_eq!(history.rows()[0].get::<String>("diff_type").unwrap(), "added");
        }
    }
    for sql in ["SELECT is_checkpoint FROM lix_commit", "SELECT is_checkpoint_active FROM lix_log()", "SELECT lixcol_commit_is_checkpoint FROM lix_history('lix_key_value')"] {
        assert!(lix.execute(sql, &[]).await.is_err(), "retired SQL must fail: {sql}");
    }
    lix.close().await.unwrap();
}