minerva 0.2.0

Causal ordering for distributed systems
//! Deterministic sealed-shadow boundary examples.

use super::*;

#[test]
fn test_the_shadow_restores_the_swept_anchor_bucket_order() {
    // S213 falsifier one: two records sharing a swept `After` bucket reverse
    // under every anchor-identity map. The shadow judges them on the old
    // shoulder, so their mutual order survives in both arrival orders (the
    // second order takes the pure fast path, the first forces a rebuild).
    let (state, movements) = anchor_case(false, [2, 1]);
    for window in [
        [Delta::Move(movements[0]), Delta::Move(movements[1])],
        [Delta::Move(movements[1]), Delta::Move(movements[0])],
    ] {
        let result = cross_boundary_shadow(&state, &[], &window);
        assert_eq!(result.fold_then_refound, ['a', 'x', 'y', 'b']);
        assert_eq!(result.shadow_projection, ['a', 'x', 'y', 'b']);
        assert!(result.commutes());
    }
}

#[test]
fn test_the_shadow_preserves_the_old_world_move_verdict() {
    // S213 falsifier two: re-chaining turns a lawful in-flight move into a
    // refused cycle-former. The shadow judges it against the old root-sibling
    // relation, where it applies, and the outcome crosses.
    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,
        },
    };

    let result = cross_boundary_shadow(&state, &[], &[Delta::Move(movement)]);
    assert_eq!(
        result.shadow_decisions,
        [Decision {
            testimony: Testimony(1),
            verdict: Verdict::Applied,
        }]
    );
    assert_eq!(result.fold_then_refound, ['b', 'a']);
    assert_eq!(result.shadow_projection, ['b', 'a']);
    assert!(result.commutes());
}

#[test]
fn test_the_shadow_carries_equal_rank_rechaining() {
    // The equal-rank arm that defeated both S213 supports: the shadow keeps
    // the old sibling context, so the dot tie-break resolves identically on
    // both squares.
    let (state, movements) = anchor_case(true, [0, 0]);
    let window = [Delta::Move(movements[0]), Delta::Move(movements[1])];
    let result = cross_boundary_shadow(&state, &[], &window);
    assert_eq!(result.fold_then_refound, ['a', 't', 'x', 'y', 'b']);
    assert_eq!(result.shadow_projection, ['a', 't', 'x', 'y', 'b']);
    assert!(result.commutes());
}

#[test]
fn test_a_window_arrival_re_decides_a_record_move() {
    // Why the shadow carries the movement record and not just the effective
    // state: the replay is order-fixed over the whole record, so a window
    // move keyed below a record move re-decides it. Here the record's
    // "a after b" stands alone at declaration, then the window's lower-keyed
    // "b after a" replays first and turns the record move into the
    // cycle-former, retroactively and deterministically.
    let mut state = State::new();
    for (index, label, rank) in [(1, 'a', 3), (2, 'b', 2), (3, 'c', 1)] {
        state.insert(
            Dot::old(index),
            Node {
                label,
                locus: Locus {
                    anchor: Anchor::Origin,
                    rank,
                },
                visible: true,
            },
        );
    }
    let record = [Movement {
        testimony: Testimony(2),
        target: Dot::old(1),
        to: Locus {
            anchor: Anchor::After(Dot::old(2)),
            rank: 2,
        },
    }];
    let window_move = Movement {
        testimony: Testimony(1),
        target: Dot::old(2),
        to: Locus {
            anchor: Anchor::After(Dot::old(1)),
            rank: 1,
        },
    };

    let mut shadow = Shadow::declare(&state, &record);
    assert_eq!(
        shadow.decisions,
        [Decision {
            testimony: Testimony(2),
            verdict: Verdict::Applied,
        }]
    );
    assert_eq!(shadow.projected_reading(), ['b', 'a', 'c']);

    shadow.deliver(Delta::Move(window_move));
    assert_eq!(
        shadow.decisions,
        [
            Decision {
                testimony: Testimony(1),
                verdict: Verdict::Applied,
            },
            Decision {
                testimony: Testimony(2),
                verdict: Verdict::RefusedCycle,
            },
        ]
    );
    assert_eq!(shadow.projected_reading(), ['a', 'b', 'c']);

    let result = cross_boundary_shadow(&state, &record, &[Delta::Move(window_move)]);
    assert!(result.commutes());
}

#[test]
fn test_an_unwitnessed_cut_lets_a_delayed_tombstone_shift_the_identity_plane() {
    // The R1 seal falsifier: admit a declaration whose cut the watermark
    // never witnessed, and a delayed at-or-below-cut tombstone from a
    // non-contributing replica splits the fold. The hasty member and the
    // informed member compact different live sets, so their maps diverge;
    // delivering the tombstone later converges the reading but can never
    // converge the identities. With identical sealed strata the fold is a
    // pure function and the map cannot shift, which is what the refusal
    // `EpochUnwitnessed` protects.
    let mut stratum = State::new();
    for (index, label, rank) in [(1, 'a', 3), (2, 'b', 2), (3, 'c', 1)] {
        stratum.insert(
            Dot::old(index),
            Node {
                label,
                locus: Locus {
                    anchor: Anchor::Origin,
                    rank,
                },
                visible: true,
            },
        );
    }
    let delayed = Delta::Delete {
        target: Dot::old(2),
    };

    let mut informed_stratum = stratum.clone();
    informed_stratum
        .nodes
        .get_mut(&Dot::old(2))
        .expect("the tombstone target is woven")
        .visible = false;

    let mut hasty = Shadow::declare(&stratum, &[]);
    let informed = Shadow::declare(&informed_stratum, &[]);
    assert_ne!(hasty.map, informed.map);
    assert_ne!(hasty.projected_reading(), informed.projected_reading());

    hasty.deliver(delayed);
    assert_eq!(hasty.projected_reading(), informed.projected_reading());
    assert_ne!(
        hasty.projected_identities(),
        informed.projected_identities()
    );

    let twin = Shadow::declare(&informed_stratum, &[]);
    assert_eq!(informed.map, twin.map);
    assert_eq!(informed.projected_identities(), twin.projected_identities());
}

#[test]
fn test_window_born_chains_survive_the_affine_shift() {
    // One live element and one tombstone at the cut: ceiling 2, one
    // compacted slot. Three consecutive window-born dots translate to three
    // consecutive new indices above the compacted range, and the map frozen
    // at declaration never shifts under the arrivals.
    let mut state = State::new();
    state.insert(
        Dot::old(1),
        Node {
            label: 'a',
            locus: Locus {
                anchor: Anchor::Origin,
                rank: 1,
            },
            visible: true,
        },
    );
    state.insert(
        Dot::old(2),
        Node {
            label: 't',
            locus: Locus {
                anchor: Anchor::After(Dot::old(1)),
                rank: 1,
            },
            visible: false,
        },
    );

    let mut shadow = Shadow::declare(&state, &[]);
    let frozen = shadow.map.clone();
    let mut anchor = Dot::old(2);
    for (index, label) in [(3, 'x'), (4, 'y'), (5, 'z')] {
        shadow.deliver(Delta::Birth {
            dot: Dot::old(index),
            label,
            locus: Locus {
                anchor: Anchor::After(anchor),
                rank: 1,
            },
        });
        anchor = Dot::old(index);
    }
    assert_eq!(shadow.map, frozen);

    let identities = shadow.projected_identities();
    let translated: Vec<Dot> = identities.iter().map(|(dot, _)| *dot).collect();
    assert_eq!(
        translated,
        [
            Dot {
                epoch: Epoch(1),
                index: 1,
            },
            Dot {
                epoch: Epoch(1),
                index: 2,
            },
            Dot {
                epoch: Epoch(1),
                index: 3,
            },
            Dot {
                epoch: Epoch(1),
                index: 4,
            },
        ]
    );
    assert_eq!(
        identities
            .iter()
            .map(|(_, label)| *label)
            .collect::<Vec<_>>(),
        ['a', 'x', 'y', 'z']
    );
}