minerva 0.2.0

Causal ordering for distributed systems
//! Span-boundary examples: [`Rhapsody::extent`]'s verdicts and the sided
//! expansion rule (PRD 0021), each a deterministic recipe an editor's mark
//! layer runs.

extern crate alloc;

use alloc::vec::Vec;

use crate::metis::{Dot, DotSet, Extent, Retired, Verge};

use super::{Seq, clock, delete, insert, weave_at};
use crate::metis::dot::RawDot;

/// Weaves `n` elements forward ("a b c ..."), returning their dots in
/// document order.
fn type_forward(replica: &mut Seq, station: u32, n: usize) -> Vec<Dot> {
    let clk = clock(station);
    let mut dots = Vec::new();
    let mut caret = None;
    for _ in 0..n {
        let dot = insert(replica, &clk, station, caret);
        dots.push(dot);
        caret = Some(dot);
    }
    dots
}

#[test]
fn test_extent_covers_the_span_between_two_verges() {
    let mut replica = Seq::new();
    let d = type_forward(&mut replica, 1, 5); // a b c d e
    let store = replica.store();

    // [Before(b), After(d)]: begins at b, ends with d.
    assert_eq!(
        store.extent(Verge::Before(d[1].into()), Verge::After(d[3].into())),
        Extent::Covered([d[1], d[2], d[3]].into()),
    );
    // [After(b), Before(d)]: strictly between them.
    assert_eq!(
        store.extent(Verge::After(d[1].into()), Verge::Before(d[3].into())),
        Extent::Covered([d[2]].into()),
    );
    // The whole document, and the one-element span.
    assert_eq!(
        store.extent(Verge::Origin, Verge::Terminus),
        Extent::Covered(store.order()),
    );
    assert_eq!(
        store.extent(Verge::Before(d[2].into()), Verge::After(d[2].into())),
        Extent::Covered([d[2]].into()),
    );
}

#[test]
fn test_an_empty_span_is_a_covered_verdict_not_an_error() {
    let mut replica = Seq::new();
    let d = type_forward(&mut replica, 1, 3); // a b c

    // Adjacent boundaries: nothing between b and its successor.
    assert_eq!(
        replica
            .store()
            .extent(Verge::After(d[1].into()), Verge::Before(d[2].into())),
        Extent::Covered(Vec::new()),
    );
    // Identical verges: the empty span by identity.
    assert_eq!(
        replica
            .store()
            .extent(Verge::After(d[1].into()), Verge::After(d[1].into())),
        Extent::Covered(Vec::new()),
    );
    // A span whose whole interior was deleted survives, currently empty:
    // emptiness is a state the span passes through, never a tombstone of
    // the mark itself.
    delete(&mut replica, d[1]);
    assert_eq!(
        replica
            .store()
            .extent(Verge::After(d[0].into()), Verge::Before(d[2].into())),
        Extent::Covered(Vec::new()),
    );
}

/// The sided expansion rule (the Peritext expand flags, structurally): which
/// element and which side a verge names decides whether an edge insert joins
/// the span, with no expansion field carried anywhere.
#[test]
fn test_edge_inserts_respect_the_sides() {
    let mut replica = Seq::new();
    let d = type_forward(&mut replica, 1, 2); // a b
    let (a, b) = (d[0], d[1]);
    let clk = clock(2);

    // Type x in the gap between a and b (the caret-after-a insert).
    let anchor = replica.store().anchor_for_visual_insert(Some(a.into()));
    if let Some(top) = replica.store().children_of(anchor).next()
        && let Some(locus) = replica.store().locus(top)
    {
        clk.observe(locus.rank);
    }
    let x = weave_at(&mut replica, &clk, 2, anchor);
    assert_eq!(replica.store().order(), [a, x, b]);
    let store = replica.store();

    // Expanding left edge: a start after a admits the gap insert.
    assert!(matches!(
        store.extent(Verge::After(a.into()), Verge::Terminus),
        Extent::Covered(ref covered) if covered.contains(&x),
    ));
    // Non-expanding left edge: a start before b still begins at b.
    assert_eq!(
        store.extent(Verge::Before(b.into()), Verge::Terminus),
        Extent::Covered([b].into()),
    );
    // Expanding right edge: an end before b admits the gap insert.
    assert_eq!(
        store.extent(Verge::Origin, Verge::Before(b.into())),
        Extent::Covered([a, x].into()),
    );
    // Non-expanding right edge: an end after a still ends with a.
    assert_eq!(
        store.extent(Verge::Origin, Verge::After(a.into())),
        Extent::Covered([a].into()),
    );
}

#[test]
fn test_a_deleted_boundary_element_still_bounds() {
    let mut replica = Seq::new();
    let d = type_forward(&mut replica, 1, 3); // a b c

    // Delete the element both spans bound through: its order tombstone
    // keeps the slot, so the boundaries hold instead of drifting (the
    // degrade alma's point-anchor recipe proved caller-side, now the
    // range read's own behavior).
    delete(&mut replica, d[1]);
    let store = replica.store();
    assert_eq!(
        store.extent(Verge::Before(d[1].into()), Verge::After(d[1].into())),
        Extent::Covered(Vec::new()),
    );
    assert_eq!(
        store.extent(Verge::After(d[0].into()), Verge::After(d[1].into())),
        Extent::Covered(Vec::new()),
    );
    assert_eq!(
        store.extent(Verge::After(d[1].into()), Verge::Terminus),
        Extent::Covered([d[2]].into()),
    );
}

#[test]
fn test_dangling_ends_are_surfaced() {
    let mut replica = Seq::new();
    let d = type_forward(&mut replica, 1, 2);

    // A verge naming a dot never woven here (the mark delta outran its
    // sequence delta), and the non-dot 0, both dangle; the flags say
    // which end. A verge is a payload coordinate, so both spellings stay
    // sayable (ruling R-91: the dangle is a read, never a refusal).
    let unwoven = RawDot::new(7, 1);
    assert_eq!(
        replica
            .store()
            .extent(Verge::After(unwoven), Verge::Terminus),
        Extent::Dangling {
            start: true,
            end: false,
        },
    );
    assert_eq!(
        replica
            .store()
            .extent(Verge::Origin, Verge::Before(unwoven)),
        Extent::Dangling {
            start: false,
            end: true,
        },
    );
    assert_eq!(
        replica.store().extent(
            Verge::After(RawDot {
                station: 1,
                counter: 0
            }),
            Verge::Before(unwoven)
        ),
        Extent::Dangling {
            start: true,
            end: true,
        },
    );
    // Placed ends do not dangle, deleted or live.
    assert!(matches!(
        replica
            .store()
            .extent(Verge::Before(d[0].into()), Verge::After(d[1].into())),
        Extent::Covered(_),
    ));
}

#[test]
fn test_condense_excision_dangles_the_verge() {
    let mut replica = Seq::new();
    let d = type_forward(&mut replica, 1, 3); // a b c

    // Delete the tail element and excise its tombstone under an applied
    // claim (nothing anchors through a tail, so it is sterile).
    delete(&mut replica, d[2]);
    let mut claim = DotSet::new();
    assert!(claim.insert(d[2]));
    let mut rhapsody = replica.store().clone();
    assert_eq!(rhapsody.condense(&Retired::trust(claim)), 1);
    let replica = crate::metis::Dotted::try_new(rhapsody, replica.context().clone()).unwrap();

    // A span pinned to the excised identity now dangles: the honest
    // degrade, surfaced rather than repaired (PRD 0021 R5; the pinning
    // policy that avoids it is the caller's, fed by
    // `Scholia::anchor_dots`).
    assert_eq!(
        replica
            .store()
            .extent(Verge::After(d[0].into()), Verge::Before(d[2].into())),
        Extent::Dangling {
            start: false,
            end: true,
        },
    );
}

#[test]
fn test_inverted_ends_are_surfaced() {
    let mut replica = Seq::new();
    let d = type_forward(&mut replica, 1, 3); // a b c
    let store = replica.store();

    // Crossed ends read backwards: a verdict, never a silent swap.
    assert_eq!(
        store.extent(Verge::After(d[2].into()), Verge::After(d[0].into())),
        Extent::Inverted,
    );
    assert_eq!(
        store.extent(Verge::Before(d[1].into()), Verge::Before(d[0].into())),
        Extent::Inverted,
    );
    // The fixed edges read in reverse, and the identity rule at a
    // positional coincidence: nothing sits before the head today, but
    // [Before(head), Origin] still reads backwards by identity,
    // deterministically at every replica.
    assert_eq!(
        store.extent(Verge::Terminus, Verge::Origin),
        Extent::Inverted
    );
    assert_eq!(
        store.extent(Verge::Before(d[0].into()), Verge::Origin),
        Extent::Inverted,
    );
    // Around one element, backwards.
    assert_eq!(
        store.extent(Verge::After(d[1].into()), Verge::Before(d[1].into())),
        Extent::Inverted,
    );
}

/// The overlap read is a derivation over extents, documented here as the
/// recipe (PRD 0021 R4): two spans overlap now iff their covered sets
/// intersect, and both sets arrive in document order.
#[test]
fn test_overlap_is_a_derivation_over_extents() {
    let mut replica = Seq::new();
    let d = type_forward(&mut replica, 1, 5); // a b c d e
    let store = replica.store();

    let left = store.extent(Verge::Before(d[0].into()), Verge::After(d[2].into()));
    let mid = store.extent(Verge::Before(d[2].into()), Verge::After(d[4].into()));
    let right = store.extent(Verge::Before(d[3].into()), Verge::After(d[4].into()));

    let overlaps = |a: &Extent, b: &Extent| -> bool {
        match (a.covered(), b.covered()) {
            (Some(xs), Some(ys)) => xs.iter().any(|x| ys.contains(x)),
            _ => false,
        }
    };
    assert!(overlaps(&left, &mid), "c is under both");
    assert!(!overlaps(&left, &right), "disjoint spans do not overlap");
}