haematite-cli 0.21.0

The haematite command: serve a store, its door and Studio, answer MCP, query with jq, and host the store's assistant seat
use std::error::Error;

use haematite::branch::ShardRef;

use super::*;
use crate::serve::protocol::Version;

type TestResult = Result<(), Box<dyn Error>>;

fn captured(branch: &str, generation: u64, sequence: u64) -> Captured {
    Captured {
        branch: branch.to_owned(),
        version: Version {
            generation,
            sequence,
        },
        shards: vec![ShardRef {
            shard_id: 0,
            fork_anchor: Hash::of(b"anchor"),
            head: Hash::of(b"head"),
        }],
    }
}

fn pins() -> Result<Pins, Failure> {
    Pins::new(
        &captured("main", 1, 2),
        &captured("main", 1, 3),
        &captured("feature", 4, 5),
    )
}

#[test]
fn full_edge_destination_changes_the_choice_token() -> TestResult {
    let pins = pins()?;
    let first = pins.token(b"e\0records\0a\0links\0records\0b", None, None, Some(b"{}"))?;
    let second = pins.token(b"e\0records\0a\0links\0records\0c", None, None, Some(b"{}"))?;
    assert_ne!(first, second);
    Ok(())
}

#[test]
fn unchanged_pins_and_raw_values_mint_the_same_token() -> TestResult {
    let key = b"e\0records\0a\0links\0records\0b";
    let first = pins()?.token(key, Some(b"{}"), None, Some(b"{\"weight\":2}"))?;
    let second = pins()?.token(key, Some(b"{}"), None, Some(b"{\"weight\":2}"))?;
    assert_eq!(first, second);
    assert_eq!(first.len(), "merge-choice-v1:".len() + 64);
    Ok(())
}

#[test]
fn changed_source_raw_bytes_change_the_token_at_the_same_pins() -> TestResult {
    let pins = pins()?;
    let key = b"e\0records\0a\0links\0records\0b";
    let first = pins.token(key, None, None, Some(b"{\"weight\":1}"))?;
    let second = pins.token(key, None, None, Some(b"{\"weight\":2}"))?;
    assert_ne!(first, second);
    Ok(())
}

#[test]
fn absent_and_present_empty_values_have_different_tokens() -> TestResult {
    let pins = pins()?;
    let key = b"d\0records\0a";
    assert_ne!(
        pins.token(key, None, None, None)?,
        pins.token(key, None, None, Some(b""))?
    );
    Ok(())
}

#[test]
fn every_value_slot_is_bound_without_json_normalisation() -> TestResult {
    let pins = pins()?;
    let key = b"d\0records\0a";
    let first = pins.token(key, Some(b"{}"), Some(b"{}"), Some(b"{}"))?;
    assert_ne!(
        first,
        pins.token(key, Some(b"{ }"), Some(b"{}"), Some(b"{}"))?
    );
    assert_ne!(
        first,
        pins.token(key, Some(b"{}"), Some(b"{ }"), Some(b"{}"))?
    );
    assert_ne!(
        first,
        pins.token(key, Some(b"{}"), Some(b"{}"), Some(b"{ }"))?
    );
    Ok(())
}

#[test]
fn branch_versions_and_physical_roots_are_bound() -> TestResult {
    let origin = captured("main", 1, 2);
    let target = captured("main", 1, 3);
    let source = captured("feature", 4, 5);
    let key = b"d\0records\0a";
    let first = Pins::new(&origin, &target, &source)?.token(key, None, None, Some(b"{}"))?;
    for slot in [0, 1, 2] {
        for field in 0..5 {
            let mut changed = [origin.clone(), target.clone(), source.clone()];
            let at = &mut changed[slot];
            match field {
                0 => at.branch.push('x'),
                1 => at.version.generation += 1,
                2 => at.version.sequence += 1,
                3 => at.shards[0].shard_id += 1,
                _ => at.shards[0].head = Hash::of(b"other head"),
            }
            let token = Pins::new(&changed[0], &changed[1], &changed[2])?.token(
                key,
                None,
                None,
                Some(b"{}"),
            )?;
            assert_ne!(first, token);
        }
    }
    Ok(())
}

#[test]
fn live_and_kept_captures_of_the_same_read_roots_have_the_same_token() -> TestResult {
    let live = [
        captured("main", 1, 2),
        captured("main", 1, 3),
        captured("feature", 4, 5),
    ];
    let mut kept = live.clone();
    for at in &mut kept {
        for shard in &mut at.shards {
            shard.fork_anchor = shard.head;
        }
    }
    let key = b"d\0records\0a";
    assert_eq!(
        Pins::new(&live[0], &live[1], &live[2])?.token(key, None, None, Some(b"{}"))?,
        Pins::new(&kept[0], &kept[1], &kept[2])?.token(key, None, None, Some(b"{}"))?
    );
    Ok(())
}