kcode-k1-persons 0.1.0

Public K1 persons transaction facade
Documentation
use std::sync::Arc;

use kcode_k1_peering::K1Peering;
use kcode_k1_persons::{K1Persons, PersonId, TxId};
use kcode_k1_txn_ordering::K1TxnOrdering;
use tempfile::TempDir;

fn open(root: &TempDir) -> (Arc<K1TxnOrdering>, Arc<K1Peering>, K1Persons) {
    let ordering = Arc::new(K1TxnOrdering::open(&root.path().join("ordering")).unwrap());
    let peering =
        Arc::new(K1Peering::open(&root.path().join("peering"), ordering.clone()).unwrap());
    let persons = K1Persons::open(root.path(), ordering.clone(), peering.clone()).unwrap();
    (ordering, peering, persons)
}

#[test]
fn lifecycle_resolves_aliases_and_replays() {
    fn require_send_sync<T: Send + Sync>() {}
    require_send_sync::<K1Persons>();
    let root = TempDir::new().unwrap();
    let (ordering, peering, persons) = open(&root);
    assert_eq!(
        persons.create(String::new()).unwrap_err(),
        "person name must be 1 through 128 UTF-8 bytes"
    );
    let first = persons.create("First".to_owned()).unwrap();
    let second = persons.create("Second".to_owned()).unwrap();
    let third = persons.create("Third".to_owned()).unwrap();
    assert_eq!(persons.resolve(first, second).unwrap(), first);
    assert_eq!(persons.get(second).unwrap().as_deref(), Some("First"));
    assert_eq!(persons.resolve(second, third).unwrap(), first);
    assert_eq!(persons.resolve(first, third).unwrap(), first);
    persons.update(third, "Unified".to_owned()).unwrap();
    for person in [first, second, third] {
        assert_eq!(persons.get(person).unwrap().as_deref(), Some("Unified"));
    }
    let unknown = PersonId::from_tx_id(TxId::from_bytes([9; 12]));
    assert_eq!(
        persons.update(unknown, "No".to_owned()).unwrap_err(),
        "person is unknown"
    );
    assert_eq!(
        persons.resolve(unknown, first).unwrap_err(),
        "canonical person is unknown"
    );
    assert_eq!(
        persons.resolve(first, unknown).unwrap_err(),
        "alias person is unknown"
    );
    drop(persons);
    drop(peering);
    drop(ordering);
    let (_, _, reopened) = open(&root);
    assert_eq!(reopened.get(first).unwrap().as_deref(), Some("Unified"));
    assert_eq!(reopened.get(second).unwrap().as_deref(), Some("Unified"));
    assert_eq!(reopened.get(third).unwrap().as_deref(), Some("Unified"));
}

#[test]
fn concurrent_identical_updates_correlate_by_transaction() {
    let root = TempDir::new().unwrap();
    let (_, _, persons) = open(&root);
    let person = persons.create("Before".to_owned()).unwrap();
    let persons = Arc::new(persons);
    let workers: Vec<_> = (0..4)
        .map(|_| {
            let persons = persons.clone();
            std::thread::spawn(move || persons.update(person, "After".to_owned()))
        })
        .collect();
    for worker in workers {
        worker.join().unwrap().unwrap();
    }
    assert_eq!(persons.get(person).unwrap().as_deref(), Some("After"));
}