minerva 0.2.0

Causal ordering for distributed systems
//! Annex-only boundary falsifiers.

use super::*;

#[test]
fn test_anchor_support_matrix_exposes_the_annex_boundary() {
    let cases = [
        (false, BoundarySupport::PositionOnly, false),
        (false, BoundarySupport::TombstoneAnnex, true),
        (true, BoundarySupport::PositionOnly, true),
        (true, BoundarySupport::TombstoneAnnex, true),
    ];

    for (anchor_is_live, support, expected) in cases {
        let (state, movements) = anchor_case(anchor_is_live, [2, 1]);
        let result = cross_boundary(&state, &movements, support);
        let applied = [
            Decision {
                testimony: Testimony(2),
                verdict: Verdict::Applied,
            },
            Decision {
                testimony: Testimony(1),
                verdict: Verdict::Applied,
            },
        ];
        assert_eq!(result.old_decisions, applied);
        assert_eq!(result.translated_decisions, applied);
        if anchor_is_live {
            assert_eq!(result.fold_then_refound, ['a', 't', 'x', 'y', 'b']);
            assert_eq!(result.refound_then_fold, ['a', 't', 'x', 'y', 'b']);
        } else if support == BoundarySupport::TombstoneAnnex {
            assert_eq!(result.fold_then_refound, ['a', 'x', 'y', 'b']);
            assert_eq!(result.refound_then_fold, ['a', 'x', 'y', 'b']);
        } else {
            assert_eq!(result.fold_then_refound, ['a', 'x', 'y', 'b']);
            assert_eq!(result.refound_then_fold, ['a', 'y', 'x', 'b']);
        }
        assert_eq!(
            result.commutes(),
            expected,
            "{anchor_is_live:?} {support:?}"
        );
    }
}

#[test]
fn test_equal_rank_rechaining_changes_live_anchor_context() {
    for support in [
        BoundarySupport::PositionOnly,
        BoundarySupport::TombstoneAnnex,
    ] {
        let (state, movements) = anchor_case(true, [0, 0]);
        let result = cross_boundary(&state, &movements, support);
        assert_eq!(result.fold_then_refound, ['a', 't', 'x', 'y', 'b']);
        assert_eq!(result.refound_then_fold, ['a', 't', 'b', 'x', 'y']);
        assert!(!result.commutes(), "{support:?}");
    }
}

#[test]
fn test_rechaining_changes_a_move_refusal_even_with_the_annex() {
    let mut state = State::new();
    let a = Dot::old(1);
    let b = Dot::old(2);
    state.insert(
        a,
        Node {
            label: 'a',
            locus: Locus {
                anchor: Anchor::Origin,
                rank: 2,
            },
            visible: true,
        },
    );
    state.insert(
        b,
        Node {
            label: 'b',
            locus: Locus {
                anchor: Anchor::Origin,
                rank: 1,
            },
            visible: true,
        },
    );
    let movement = Movement {
        testimony: Testimony(1),
        target: a,
        to: Locus {
            anchor: Anchor::After(b),
            rank: 1,
        },
    };

    for support in [
        BoundarySupport::PositionOnly,
        BoundarySupport::TombstoneAnnex,
    ] {
        let result = cross_boundary(&state, &[movement], support);
        assert_eq!(
            result.old_decisions,
            [Decision {
                testimony: Testimony(1),
                verdict: Verdict::Applied,
            }]
        );
        assert_eq!(
            result.translated_decisions,
            [Decision {
                testimony: Testimony(1),
                verdict: Verdict::RefusedCycle,
            }]
        );
        assert_eq!(result.fold_then_refound, ['b', 'a']);
        assert_eq!(result.refound_then_fold, ['a', 'b']);
        assert!(!result.commutes());
    }
}

#[test]
fn test_replay_order_decides_which_cycle_former_is_refused() {
    let mut state = State::new();
    let a = Dot::old(1);
    let b = Dot::old(2);
    let c = Dot::old(3);
    for (dot, label, rank) in [(a, 'a', 3), (b, 'b', 2), (c, 'c', 1)] {
        state.insert(
            dot,
            Node {
                label,
                locus: Locus {
                    anchor: Anchor::Origin,
                    rank,
                },
                visible: true,
            },
        );
    }

    let mut movements = [
        Movement {
            testimony: Testimony(3),
            target: a,
            to: Locus {
                anchor: Anchor::After(c),
                rank: 3,
            },
        },
        Movement {
            testimony: Testimony(2),
            target: c,
            to: Locus {
                anchor: Anchor::After(b),
                rank: 2,
            },
        },
        Movement {
            testimony: Testimony(1),
            target: b,
            to: Locus {
                anchor: Anchor::After(a),
                rank: 1,
            },
        },
    ];

    let expected = [
        Decision {
            testimony: Testimony(1),
            verdict: Verdict::Applied,
        },
        Decision {
            testimony: Testimony(2),
            verdict: Verdict::Applied,
        },
        Decision {
            testimony: Testimony(3),
            verdict: Verdict::RefusedCycle,
        },
    ];

    let mut rank_ordered = state.clone();
    assert_eq!(replay(&mut rank_ordered, &movements), expected);
    assert_eq!(rank_ordered.reading(), ['a', 'b', 'c']);

    for movement in &mut movements {
        movement.to.rank = 1;
    }
    assert_eq!(replay(&mut state, &movements), expected);
    assert_eq!(state.reading(), ['a', 'b', 'c']);
}