minerva 0.2.0

Causal ordering for distributed systems
use super::super::super::support::dot;
use crate::metis::{Composer, DotMap, DotSet, DotStore, Dotted};

use super::{Doc, add_under, assert_covered, dots};

#[test]
fn test_compose_assigns_the_first_dot_and_holds_it() {
    let mut composer: Composer<Doc> = Composer::new(1);
    assert_eq!(composer.station(), 1);
    let (receipt, delta) = composer.compose(add_under(7));
    assert_eq!(receipt, dot(1, 1));
    assert!(delta.context().contains(receipt));
    assert!(
        delta
            .store()
            .get(&7)
            .is_some_and(|held| held.contains(dot(1, 1)))
    );
    assert!(
        composer
            .state()
            .store()
            .get(&7)
            .is_some_and(|held| held.contains(dot(1, 1)))
    );
    assert_eq!(composer.state().next_dot(1), dot(1, 2));
}

#[test]
fn test_a_run_write_composes_like_a_keystroke() {
    // The run-grade write (R-19) rides `compose` unchanged: the closure
    // weaves a whole reservation from the receipt dot, the delta covers
    // every run dot, both disciplines (freshness past the run, covered
    // store) hold by the same construction, and a shipped run delta
    // converges an absorbing peer.
    extern crate alloc;
    use crate::kairos::{Clock, TickCounter};
    use crate::metis::{Anchor, Rhapsody};

    let clock = Clock::with_default_config(TickCounter::new(), 1).unwrap();
    let mut writer: Composer<Rhapsody> = Composer::new(1);
    let run = clock.now_run(core::num::NonZeroU32::new(150).unwrap(), 0u16);
    let (receipt, delta) = writer.compose(|assigned| {
        let mut rhapsody = Rhapsody::new();
        assert!(rhapsody.weave_run(assigned, Anchor::Origin, run));
        (rhapsody, DotSet::new())
    });
    assert_eq!(receipt, dot(1, 1));
    for k in 1..=150u64 {
        assert!(
            delta.context().contains(dot(1, k)),
            "the delta covers the run"
        );
    }
    assert!(
        delta
            .store()
            .dots()
            .all(|held| delta.context().contains(held)),
        "the delta's context covers its store"
    );
    assert_eq!(
        writer.state().next_dot(1),
        dot(1, 151),
        "the next assignment is fresh past the whole run"
    );
    assert_eq!(writer.state().store().order().len(), 150);

    let mut reader: Composer<Rhapsody> = Composer::new(2);
    let _ = reader.absorb(1, &delta);
    assert_eq!(reader.state(), writer.state(), "one run delta converges");
}

#[test]
fn test_compose_calls_ascend_strictly() {
    let mut composer: Composer<Doc> = Composer::new(1);
    let (d1, first) = composer.compose(add_under(7));
    let (d2, second) = composer.compose(add_under(7));
    let (d3, third) = composer.compose(add_under(9));
    assert_eq!((d1, d2, d3), (dot(1, 1), dot(1, 2), dot(1, 3)));
    assert!(first.store().get(&7).is_some_and(|h| h.contains(dot(1, 1))));
    assert!(
        second
            .store()
            .get(&7)
            .is_some_and(|h| h.contains(dot(1, 2)))
    );
    assert!(third.store().get(&9).is_some_and(|h| h.contains(dot(1, 3))));
}

#[test]
fn test_compose_after_retracting_everything_stays_fresh() {
    let mut composer: Composer<Doc> = Composer::new(1);
    let _ = composer.compose(add_under(7));
    let observed = composer
        .state()
        .store()
        .get(&7)
        .cloned()
        .unwrap_or_default();
    let _ = composer.retract(observed);
    assert!(composer.state().store().is_bottom());

    let (_, after) = composer.compose(add_under(7));
    assert!(
        after
            .store()
            .get(&7)
            .is_some_and(|held| held.contains(dot(1, 2)))
    );
    assert!(
        !after
            .store()
            .get(&7)
            .is_some_and(|held| held.contains(dot(1, 1)))
    );
}

#[test]
fn test_compose_that_supersedes_is_covered() {
    let mut composer: Composer<Doc> = Composer::new(1);
    let _ = composer.compose(add_under(7));
    let old = composer
        .state()
        .store()
        .get(&7)
        .cloned()
        .unwrap_or_default();
    let (_, delta) = composer.compose(move |assigned| {
        let store = DotMap::singleton(7u8, dots(&[(assigned.station(), assigned.counter())]));
        (store, old)
    });
    assert!(delta.context().contains(dot(1, 1)));
    assert!(delta.context().contains(dot(1, 2)));
    assert!(
        delta
            .store()
            .get(&7)
            .is_some_and(|held| held.contains(dot(1, 2)))
    );
    assert_covered(&delta);

    let held = composer.state().store().get(&7).expect("the key survives");
    assert!(held.contains(dot(1, 2)) && !held.contains(dot(1, 1)));
}

#[test]
fn test_retract_and_owed_deltas_are_covered() {
    let mut composer: Composer<Doc> = Composer::new(1);
    let _ = composer.compose(add_under(7));
    let observed = composer
        .state()
        .store()
        .get(&7)
        .cloned()
        .unwrap_or_default();
    let retraction = composer.retract(observed);
    assert_covered(&retraction);
    let owed = composer.owed_to(&DotSet::new());
    assert_covered(&owed);
    assert_eq!(&owed, composer.state());
}

#[test]
fn test_compose_receipt_is_the_merged_dot_even_with_a_merge_around_the_call() {
    let mut composer: Composer<Doc> = Composer::new(1);
    let foreign = Dotted::from_store(DotMap::singleton(3u8, dots(&[(1, 1), (1, 2)])));
    let _ = composer.absorb(9, &foreign);

    let (receipt, delta) = composer.compose(add_under(7));
    assert_eq!(
        receipt,
        dot(1, 3),
        "the receipt is the true high-water successor"
    );
    assert!(
        delta
            .store()
            .get(&7)
            .is_some_and(|held| held.contains(receipt)),
        "the store carries exactly the receipt dot"
    );
    assert!(
        composer
            .state()
            .store()
            .get(&7)
            .is_some_and(|held| held.contains(dot(1, 3)))
    );
}

#[test]
fn test_compose_super_equals_the_manual_read_then_compose() {
    let mut manual: Composer<Doc> = Composer::new(1);
    let mut facade: Composer<Doc> = Composer::new(1);
    let _ = manual.compose(add_under(7));
    let _ = facade.compose(add_under(7));

    let observed = manual.state().store().get(&7).cloned().unwrap_or_default();
    let (manual_dot, manual_delta) = manual.compose(move |assigned| {
        (
            DotMap::singleton(7u8, dots(&[(assigned.station(), assigned.counter())])),
            observed,
        )
    });

    let (facade_dot, facade_delta) = facade.compose_super(
        |assigned| DotMap::singleton(7u8, dots(&[(assigned.station(), assigned.counter())])),
        |held| held.get(&7).cloned().unwrap_or_default(),
    );

    assert_eq!(manual_dot, facade_dot);
    assert_eq!(manual_delta, facade_delta);
    assert_eq!(manual.state(), facade.state());
    assert!(facade_delta.context().contains(dot(1, 1)));
    assert!(facade_delta.context().contains(dot(1, 2)));
}