minerva 0.2.0

Causal ordering for distributed systems
extern crate alloc;

use super::{Doc, Op, SCRIBES, arb_ops, observed};
use crate::metis::{Composer, DotMap, DotSet, Dotted};
use alloc::vec::Vec;
use proptest::prelude::*;

proptest! {
    #[test]
    fn prop_scribe_matches_the_manual_flow(ops in arb_ops()) {
        let mut composers: Vec<Composer<Doc>> = (0..SCRIBES)
            .map(|i| Composer::new(u32::try_from(i).unwrap()))
            .collect();
        let mut manual: Vec<Dotted<Doc>> = (0..SCRIBES).map(|_| Dotted::new()).collect();

        for op in &ops {
            match *op {
                Op::Add { composer, key } => {
                    let (via_dot, via_scribe) = composers[composer].compose(move |assigned| {
                        let mut held = DotSet::new();
                        let _ = held.insert(assigned);
                        (DotMap::singleton(key, held), DotSet::new())
                    });

                    let station = u32::try_from(composer).unwrap();
                    let assigned = manual[composer].next_dot(station);
                    let mut held = DotSet::new();
                    let _ = held.insert(assigned);
                    let by_hand = Dotted::from_store(DotMap::singleton(key, held));
                    manual[composer] = manual[composer].merge(&by_hand);

                    prop_assert_eq!(&via_scribe, &by_hand);
                    prop_assert_eq!(via_dot, assigned);
                    prop_assert_eq!(composers[composer].state(), &manual[composer]);
                }
                Op::Remove { composer, key } => {
                    let seen = observed(composers[composer].state(), key);
                    let via_scribe = composers[composer].retract(seen);
                    let obs = observed(&manual[composer], key);
                    let by_hand = Dotted::from_context(obs);
                    manual[composer] = manual[composer].merge(&by_hand);

                    prop_assert_eq!(&via_scribe, &by_hand);
                    prop_assert_eq!(composers[composer].state(), &manual[composer]);
                }
                Op::Gossip { from, to } => {
                    let scribe_delta = composers[from].owed_to(composers[to].state().context());
                    let _ = composers[to].absorb(u32::try_from(from).unwrap(), &scribe_delta);
                    let hand_delta = manual[from].delta_for(manual[to].context());
                    manual[to] = manual[to].merge(&hand_delta);

                    prop_assert_eq!(&scribe_delta, &hand_delta);
                    prop_assert_eq!(composers[to].state(), &manual[to]);
                }
            }
        }
    }
}